You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

170 lines
3.6 KiB

package discovery
import (
"context"
"sonet/pkg/utils/logger"
"time"
clientv3 "go.etcd.io/etcd/client/v3"
"google.golang.org/grpc/resolver"
)
const (
schema = "etcd"
)
// Resolver for grpc client
type Resolver struct {
schema string
DialTimeout int
closeCh chan struct{}
watchCh clientv3.WatchChan
cli *clientv3.Client
keyPrifix string
srvAddrsList []resolver.Address
cc resolver.ClientConn
}
// NewResolver create a new resolver.Builder base on etcd
func NewResolver(client *clientv3.Client) *Resolver {
return &Resolver{
cli: client,
schema: schema,
DialTimeout: 3,
}
}
// Scheme returns the scheme supported by this resolver.
func (r *Resolver) Scheme() string {
return r.schema
}
// Build creates a new resolver.Resolver for the given target
func (r *Resolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (rr resolver.Resolver, err error) {
r.cc = cc
r.keyPrifix = BuildPrefix(Server{Name: target.Endpoint()})
if err = r.start(); err != nil {
return nil, err
}
return r, nil
}
// ResolveNow resolver.Resolver interface
func (r *Resolver) ResolveNow(o resolver.ResolveNowOptions) {}
// Close resolver.Resolver interface
func (r *Resolver) Close() {
if r.closeCh == nil {
return
}
r.closeCh <- struct{}{}
<-r.closeCh
}
// start
func (r *Resolver) start() error {
var err error
resolver.Register(r)
if r.closeCh == nil {
r.closeCh = make(chan struct{})
}
if err = r.sync(); err != nil {
return err
}
go r.watch()
return nil
}
// watch update events
func (r *Resolver) watch() {
ticker := time.NewTicker(time.Minute)
r.watchCh = r.cli.Watch(context.Background(), r.keyPrifix, clientv3.WithPrefix())
for {
select {
case <-r.closeCh:
r.closeCh <- struct{}{}
return
case res, ok := <-r.watchCh:
if ok {
r.update(res.Events)
}
case <-ticker.C:
if err := r.sync(); err != nil {
logger.Error("resolver sync failed: ", err)
}
}
}
}
// update
func (r *Resolver) update(events []*clientv3.Event) {
for _, ev := range events {
var info Server
var err error
switch ev.Type {
case clientv3.EventTypePut:
info, err = ParseValue(ev.Kv.Value)
if err != nil {
continue
}
addr := resolver.Address{Addr: info.Addr}
for k, v := range info.Attrs {
addr.Attributes = addr.Attributes.WithValue(k, v)
}
if !Exist(r.srvAddrsList, addr) {
r.srvAddrsList = append(r.srvAddrsList, addr)
err = r.cc.UpdateState(resolver.State{Addresses: r.srvAddrsList})
if err != nil {
logger.Error("resolver conn update put state err: ", err)
}
}
case clientv3.EventTypeDelete:
info, err = SplitPath(string(ev.Kv.Key))
if err != nil {
continue
}
addr := resolver.Address{Addr: info.Addr}
if s, ok := Remove(r.srvAddrsList, addr); ok {
r.srvAddrsList = s
err = r.cc.UpdateState(resolver.State{Addresses: r.srvAddrsList})
if err != nil {
logger.Error("resolver conn update delete state err: ", err)
}
}
}
}
}
// sync 同步获取所有地址信息
func (r *Resolver) sync() (err error) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
res, err := r.cli.Get(ctx, r.keyPrifix, clientv3.WithPrefix())
if err != nil {
return
}
r.srvAddrsList = []resolver.Address{}
for _, v := range res.Kvs {
info, err := ParseValue(v.Value)
if err != nil {
continue
}
addr := resolver.Address{Addr: info.Addr}
for k, v := range info.Attrs {
addr.Attributes = addr.Attributes.WithValue(k, v)
}
r.srvAddrsList = append(r.srvAddrsList, addr)
}
err = r.cc.UpdateState(resolver.State{Addresses: r.srvAddrsList})
return
}