2019-07-18 17:32:34 +08:00
package etcd
import (
"context"
"encoding/json"
"errors"
2019-07-19 21:48:22 +08:00
"flag"
2019-07-18 17:32:34 +08:00
"fmt"
2019-07-19 21:48:22 +08:00
"os"
"strings"
2019-07-18 17:32:34 +08:00
"sync"
"sync/atomic"
2019-07-18 17:33:07 +08:00
"time"
2019-07-18 17:32:34 +08:00
2020-03-28 18:02:25 +08:00
"github.com/go-kratos/kratos/pkg/log"
"github.com/go-kratos/kratos/pkg/naming"
2019-07-22 11:39:09 +08:00
"go.etcd.io/etcd/clientv3"
"go.etcd.io/etcd/mvcc/mvccpb"
2019-07-22 13:09:34 +08:00
"google.golang.org/grpc"
2019-07-18 17:32:34 +08:00
)
2019-07-18 17:33:07 +08:00
2019-07-19 21:48:22 +08:00
var (
2019-07-19 22:02:25 +08:00
//etcdPrefix is a etcd globe key prefix
endpoints string
etcdPrefix string
2019-07-18 17:32:34 +08:00
2019-07-22 11:39:09 +08:00
//Time units is second
2019-07-22 13:42:08 +08:00
registerTTL = 90
2019-07-22 11:39:09 +08:00
defaultDialTimeout = 30
2019-07-18 17:32:34 +08:00
)
2019-07-18 17:33:07 +08:00
2019-07-18 17:32:34 +08:00
var (
_once sync . Once
_builder naming . Builder
2019-07-18 17:33:07 +08:00
//ErrDuplication is a register duplication err
2019-07-18 17:32:34 +08:00
ErrDuplication = errors . New ( "etcd: instance duplicate registration" )
)
2019-07-19 21:48:22 +08:00
func init ( ) {
addFlag ( flag . CommandLine )
}
func addFlag ( fs * flag . FlagSet ) {
// env
2019-07-19 22:02:25 +08:00
fs . StringVar ( & endpoints , "etcd.endpoints" , os . Getenv ( "ETCD_ENDPOINTS" ) , "etcd.endpoints is etcd endpoints. value: 127.0.0.1:2379,127.0.0.2:2379 etc." )
fs . StringVar ( & etcdPrefix , "etcd.prefix" , defaultString ( "ETCD_PREFIX" , "kratos_etcd" ) , "etcd globe key prefix or use ETCD_PREFIX env variable. value etcd_prefix etc." )
2019-07-19 21:48:22 +08:00
}
func defaultString ( env , value string ) string {
v := os . Getenv ( env )
if v == "" {
return value
}
return v
}
2019-07-18 17:32:34 +08:00
// Builder return default etcd resolver builder.
func Builder ( c * clientv3 . Config ) naming . Builder {
_once . Do ( func ( ) {
2019-07-19 21:48:22 +08:00
_builder , _ = New ( c )
2019-07-18 17:32:34 +08:00
} )
return _builder
}
2019-07-18 17:33:07 +08:00
2019-07-18 17:32:34 +08:00
// Build register resolver into default etcd.
2019-07-18 17:33:07 +08:00
func Build ( c * clientv3 . Config , id string ) naming . Resolver {
2019-07-18 17:32:34 +08:00
return Builder ( c ) . Build ( id )
}
2019-07-18 17:33:07 +08:00
2019-07-19 21:48:22 +08:00
// EtcdBuilder is a etcd clientv3 EtcdBuilder
2019-07-18 17:32:34 +08:00
type EtcdBuilder struct {
2019-07-18 17:33:07 +08:00
cli * clientv3 . Client
2019-07-18 17:32:34 +08:00
ctx context . Context
cancelFunc context . CancelFunc
2019-07-18 17:33:07 +08:00
mutex sync . RWMutex
apps map [ string ] * appInfo
registry map [ string ] struct { }
2019-07-18 17:32:34 +08:00
}
type appInfo struct {
resolver map [ * Resolve ] struct { }
2019-07-18 17:33:07 +08:00
ins atomic . Value
e * EtcdBuilder
once sync . Once
2019-07-18 17:32:34 +08:00
}
2019-07-18 17:33:07 +08:00
2019-07-18 17:32:34 +08:00
// Resolve etch resolver.
type Resolve struct {
id string
event chan struct { }
e * EtcdBuilder
2019-10-23 10:50:29 +08:00
opt * naming . BuildOptions
2019-07-18 17:32:34 +08:00
}
2019-07-19 21:48:22 +08:00
// New is new a etcdbuilder
func New ( c * clientv3 . Config ) ( e * EtcdBuilder , err error ) {
if c == nil {
2019-07-19 22:02:25 +08:00
if endpoints == "" {
panic ( fmt . Errorf ( "invalid etcd config endpoints:%+v" , endpoints ) )
2019-07-19 21:48:22 +08:00
}
c = & clientv3 . Config {
2019-07-19 22:02:25 +08:00
Endpoints : strings . Split ( endpoints , "," ) ,
2019-07-22 11:39:09 +08:00
DialTimeout : time . Second * time . Duration ( defaultDialTimeout ) ,
2019-07-22 13:09:34 +08:00
DialOptions : [ ] grpc . DialOption { grpc . WithBlock ( ) } ,
2019-07-19 21:48:22 +08:00
}
}
2019-07-18 17:33:07 +08:00
cli , err := clientv3 . New ( * c )
if err != nil {
2019-07-19 21:48:22 +08:00
return nil , err
2019-07-18 17:32:34 +08:00
}
ctx , cancel := context . WithCancel ( context . Background ( ) )
e = & EtcdBuilder {
2019-07-18 17:33:07 +08:00
cli : cli ,
2019-07-18 17:32:34 +08:00
ctx : ctx ,
cancelFunc : cancel ,
apps : map [ string ] * appInfo { } ,
registry : map [ string ] struct { } { } ,
}
return
}
// Build disovery resovler builder.
2019-10-23 10:50:29 +08:00
func ( e * EtcdBuilder ) Build ( appid string , opts ... naming . BuildOpt ) naming . Resolver {
2019-07-18 17:32:34 +08:00
r := & Resolve {
id : appid ,
e : e ,
event : make ( chan struct { } , 1 ) ,
2019-10-23 10:50:29 +08:00
opt : new ( naming . BuildOptions ) ,
2019-07-18 17:32:34 +08:00
}
e . mutex . Lock ( )
app , ok := e . apps [ appid ]
if ! ok {
app = & appInfo {
resolver : make ( map [ * Resolve ] struct { } ) ,
2019-07-18 17:33:07 +08:00
e : e ,
2019-07-18 17:32:34 +08:00
}
e . apps [ appid ] = app
}
app . resolver [ r ] = struct { } { }
e . mutex . Unlock ( )
if ok {
select {
case r . event <- struct { } { } :
default :
}
}
2019-07-18 17:33:07 +08:00
app . once . Do ( func ( ) {
2019-07-18 17:32:34 +08:00
go app . watch ( appid )
log . Info ( "etcd: AddWatch(%s) already watch(%v)" , appid , ok )
} )
return r
}
// Scheme return etcd's scheme
func ( e * EtcdBuilder ) Scheme ( ) string {
return "etcd"
}
2019-07-19 21:48:22 +08:00
// Register is register instance
2019-07-18 17:32:34 +08:00
func ( e * EtcdBuilder ) Register ( ctx context . Context , ins * naming . Instance ) ( cancelFunc context . CancelFunc , err error ) {
e . mutex . Lock ( )
if _ , ok := e . registry [ ins . AppID ] ; ok {
err = ErrDuplication
} else {
e . registry [ ins . AppID ] = struct { } { }
}
e . mutex . Unlock ( )
if err != nil {
return
}
ctx , cancel := context . WithCancel ( e . ctx )
if err = e . register ( ctx , ins ) ; err != nil {
e . mutex . Lock ( )
delete ( e . registry , ins . AppID )
e . mutex . Unlock ( )
cancel ( )
return
}
ch := make ( chan struct { } , 1 )
cancelFunc = context . CancelFunc ( func ( ) {
cancel ( )
<- ch
} )
go func ( ) {
2019-07-22 13:42:08 +08:00
ticker := time . NewTicker ( time . Duration ( registerTTL / 3 ) * time . Second )
2019-07-18 17:32:34 +08:00
defer ticker . Stop ( )
for {
select {
case <- ticker . C :
_ = e . register ( ctx , ins )
case <- ctx . Done ( ) :
_ = e . unregister ( ins )
ch <- struct { } { }
return
}
}
} ( )
return
}
2019-07-18 17:33:07 +08:00
2019-07-18 17:32:34 +08:00
//注册和续约公用一个操作
2019-07-18 17:33:07 +08:00
func ( e * EtcdBuilder ) register ( ctx context . Context , ins * naming . Instance ) ( err error ) {
2019-07-19 21:48:22 +08:00
prefix := e . keyPrefix ( ins )
2019-07-18 17:33:07 +08:00
val , _ := json . Marshal ( ins )
2019-07-18 17:32:34 +08:00
2019-07-22 11:39:09 +08:00
ttlResp , err := e . cli . Grant ( context . TODO ( ) , int64 ( registerTTL ) )
2019-07-18 17:33:07 +08:00
if err != nil {
2019-07-22 11:39:09 +08:00
log . Error ( "etcd: register client.Grant(%v) error(%v)" , registerTTL , err )
2019-07-18 17:32:34 +08:00
return err
}
2019-07-18 17:33:07 +08:00
_ , err = e . cli . Put ( ctx , prefix , string ( val ) , clientv3 . WithLease ( ttlResp . ID ) )
if err != nil {
2019-07-18 17:32:34 +08:00
log . Error ( "etcd: register client.Put(%v) appid(%s) hostname(%s) error(%v)" ,
2019-07-18 17:33:07 +08:00
prefix , ins . AppID , ins . Hostname , err )
2019-07-18 17:32:34 +08:00
return err
}
return nil
}
2019-07-18 17:33:07 +08:00
func ( e * EtcdBuilder ) unregister ( ins * naming . Instance ) ( err error ) {
2019-07-19 21:48:22 +08:00
prefix := e . keyPrefix ( ins )
2019-07-18 17:32:34 +08:00
2019-07-18 17:33:07 +08:00
if _ , err = e . cli . Delete ( context . TODO ( ) , prefix ) ; err != nil {
2019-07-18 17:32:34 +08:00
log . Error ( "etcd: unregister client.Delete(%v) appid(%s) hostname(%s) error(%v)" ,
prefix , ins . AppID , ins . Hostname , err )
}
log . Info ( "etcd: unregister client.Delete(%v) appid(%s) hostname(%s) success" ,
prefix , ins . AppID , ins . Hostname )
return
}
2019-07-19 21:48:22 +08:00
func ( e * EtcdBuilder ) keyPrefix ( ins * naming . Instance ) string {
2019-07-19 22:02:25 +08:00
return fmt . Sprintf ( "/%s/%s/%s" , etcdPrefix , ins . AppID , ins . Hostname )
2019-07-18 17:32:34 +08:00
}
2019-07-18 17:33:07 +08:00
2019-07-18 17:32:34 +08:00
// Close stop all running process including etcdfetch and register
func ( e * EtcdBuilder ) Close ( ) error {
e . cancelFunc ( )
return nil
}
2019-07-18 17:33:07 +08:00
func ( a * appInfo ) watch ( appID string ) {
2019-07-18 17:32:34 +08:00
_ = a . fetchstore ( appID )
2019-07-19 22:02:25 +08:00
prefix := fmt . Sprintf ( "/%s/%s/" , etcdPrefix , appID )
2019-07-18 17:32:34 +08:00
rch := a . e . cli . Watch ( a . e . ctx , prefix , clientv3 . WithPrefix ( ) )
for wresp := range rch {
for _ , ev := range wresp . Events {
2019-07-18 17:33:07 +08:00
if ev . Type == mvccpb . PUT || ev . Type == mvccpb . DELETE {
2019-07-18 17:32:34 +08:00
_ = a . fetchstore ( appID )
}
}
}
}
2019-07-18 17:33:07 +08:00
func ( a * appInfo ) fetchstore ( appID string ) ( err error ) {
2019-07-19 22:02:25 +08:00
prefix := fmt . Sprintf ( "/%s/%s/" , etcdPrefix , appID )
2019-07-18 17:33:07 +08:00
resp , err := a . e . cli . Get ( a . e . ctx , prefix , clientv3 . WithPrefix ( ) )
if err != nil {
2019-07-18 17:32:34 +08:00
log . Error ( "etcd: fetch client.Get(%s) error(%+v)" , prefix , err )
return err
}
2019-07-18 17:33:07 +08:00
ins , err := a . paserIns ( resp )
if err != nil {
2019-07-18 17:32:34 +08:00
return err
}
a . store ( ins )
return nil
}
2019-07-18 17:33:07 +08:00
func ( a * appInfo ) store ( ins * naming . InstancesInfo ) {
2019-07-18 17:32:34 +08:00
2019-07-18 17:33:07 +08:00
a . ins . Store ( ins )
a . e . mutex . RLock ( )
for rs := range a . resolver {
select {
case rs . event <- struct { } { } :
default :
2019-07-18 17:32:34 +08:00
}
2019-07-18 17:33:07 +08:00
}
a . e . mutex . RUnlock ( )
2019-07-18 17:32:34 +08:00
}
2019-07-18 17:33:07 +08:00
func ( a * appInfo ) paserIns ( resp * clientv3 . GetResponse ) ( ins * naming . InstancesInfo , err error ) {
2019-07-18 17:32:34 +08:00
ins = & naming . InstancesInfo {
2019-07-18 17:33:07 +08:00
Instances : make ( map [ string ] [ ] * naming . Instance , 0 ) ,
2019-07-18 17:32:34 +08:00
}
for _ , ev := range resp . Kvs {
in := new ( naming . Instance )
2019-07-18 17:33:07 +08:00
err := json . Unmarshal ( ev . Value , in )
if err != nil {
return nil , err
2019-07-18 17:32:34 +08:00
}
2019-07-18 17:33:07 +08:00
ins . Instances [ in . Zone ] = append ( ins . Instances [ in . Zone ] , in )
2019-07-18 17:32:34 +08:00
}
2019-07-18 17:33:07 +08:00
return ins , nil
2019-07-18 17:32:34 +08:00
}
2019-07-18 17:33:07 +08:00
2019-07-18 17:32:34 +08:00
// Watch watch instance.
func ( r * Resolve ) Watch ( ) <- chan struct { } {
return r . event
}
// Fetch fetch resolver instance.
func ( r * Resolve ) Fetch ( ctx context . Context ) ( ins * naming . InstancesInfo , ok bool ) {
r . e . mutex . RLock ( )
app , ok := r . e . apps [ r . id ]
r . e . mutex . RUnlock ( )
if ok {
ins , ok = app . ins . Load ( ) . ( * naming . InstancesInfo )
return
}
return
}
// Close close resolver.
func ( r * Resolve ) Close ( ) error {
r . e . mutex . Lock ( )
if app , ok := r . e . apps [ r . id ] ; ok && len ( app . resolver ) != 0 {
delete ( app . resolver , r )
}
r . e . mutex . Unlock ( )
return nil
}