gitee.com/liuxuezhan/go-micro-v1.18.0@v1.0.0/registry/etcd/etcd.go (about)

     1  // Package etcd provides an etcd service registry
     2  package etcd
     3  
     4  import (
     5  	"context"
     6  	"crypto/tls"
     7  	"encoding/json"
     8  	"errors"
     9  	"net"
    10  	"path"
    11  	"sort"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/coreos/etcd/clientv3"
    17  	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
    18  	"gitee.com/liuxuezhan/go-micro-v1.18.0/registry"
    19  	"gitee.com/liuxuezhan/go-micro-v1.18.0/util/log"
    20  	hash "github.com/mitchellh/hashstructure"
    21  )
    22  
    23  var (
    24  	prefix = "/micro/registry/"
    25  )
    26  
    27  type etcdRegistry struct {
    28  	client  *clientv3.Client
    29  	options registry.Options
    30  
    31  	sync.RWMutex
    32  	register map[string]uint64
    33  	leases   map[string]clientv3.LeaseID
    34  }
    35  
    36  func NewRegistry(opts ...registry.Option) registry.Registry {
    37  	e := &etcdRegistry{
    38  		options:  registry.Options{},
    39  		register: make(map[string]uint64),
    40  		leases:   make(map[string]clientv3.LeaseID),
    41  	}
    42  	configure(e, opts...)
    43  	return e
    44  }
    45  
    46  func configure(e *etcdRegistry, opts ...registry.Option) error {
    47  	config := clientv3.Config{
    48  		Endpoints: []string{"127.0.0.1:2379"},
    49  	}
    50  
    51  	for _, o := range opts {
    52  		o(&e.options)
    53  	}
    54  
    55  	if e.options.Timeout == 0 {
    56  		e.options.Timeout = 5 * time.Second
    57  	}
    58  
    59  	if e.options.Secure || e.options.TLSConfig != nil {
    60  		tlsConfig := e.options.TLSConfig
    61  		if tlsConfig == nil {
    62  			tlsConfig = &tls.Config{
    63  				InsecureSkipVerify: true,
    64  			}
    65  		}
    66  
    67  		config.TLS = tlsConfig
    68  	}
    69  
    70  	if e.options.Context != nil {
    71  		u, ok := e.options.Context.Value(authKey{}).(*authCreds)
    72  		if ok {
    73  			config.Username = u.Username
    74  			config.Password = u.Password
    75  		}
    76  	}
    77  
    78  	var cAddrs []string
    79  
    80  	for _, address := range e.options.Addrs {
    81  		if len(address) == 0 {
    82  			continue
    83  		}
    84  		addr, port, err := net.SplitHostPort(address)
    85  		if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
    86  			port = "2379"
    87  			addr = address
    88  			cAddrs = append(cAddrs, net.JoinHostPort(addr, port))
    89  		} else if err == nil {
    90  			cAddrs = append(cAddrs, net.JoinHostPort(addr, port))
    91  		}
    92  	}
    93  
    94  	// if we got addrs then we'll update
    95  	if len(cAddrs) > 0 {
    96  		config.Endpoints = cAddrs
    97  	}
    98  
    99  	cli, err := clientv3.New(config)
   100  	if err != nil {
   101  		return err
   102  	}
   103  	e.client = cli
   104  	return nil
   105  }
   106  
   107  func encode(s *registry.Service) string {
   108  	b, _ := json.Marshal(s)
   109  	return string(b)
   110  }
   111  
   112  func decode(ds []byte) *registry.Service {
   113  	var s *registry.Service
   114  	json.Unmarshal(ds, &s)
   115  	return s
   116  }
   117  
   118  func nodePath(s, id string) string {
   119  	service := strings.Replace(s, "/", "-", -1)
   120  	node := strings.Replace(id, "/", "-", -1)
   121  	return path.Join(prefix, service, node)
   122  }
   123  
   124  func servicePath(s string) string {
   125  	return path.Join(prefix, strings.Replace(s, "/", "-", -1))
   126  }
   127  
   128  func (e *etcdRegistry) Init(opts ...registry.Option) error {
   129  	return configure(e, opts...)
   130  }
   131  
   132  func (e *etcdRegistry) Options() registry.Options {
   133  	return e.options
   134  }
   135  
   136  func (e *etcdRegistry) registerNode(s *registry.Service, node *registry.Node, opts ...registry.RegisterOption) error {
   137  	if len(s.Nodes) == 0 {
   138  		return errors.New("Require at least one node")
   139  	}
   140  
   141  	// check existing lease cache
   142  	e.RLock()
   143  	leaseID, ok := e.leases[s.Name+node.Id]
   144  	e.RUnlock()
   145  
   146  	if !ok {
   147  		// missing lease, check if the key exists
   148  		ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
   149  		defer cancel()
   150  
   151  		// look for the existing key
   152  		rsp, err := e.client.Get(ctx, nodePath(s.Name, node.Id), clientv3.WithSerializable())
   153  		if err != nil {
   154  			return err
   155  		}
   156  
   157  		// get the existing lease
   158  		for _, kv := range rsp.Kvs {
   159  			if kv.Lease > 0 {
   160  				leaseID = clientv3.LeaseID(kv.Lease)
   161  
   162  				// decode the existing node
   163  				srv := decode(kv.Value)
   164  				if srv == nil || len(srv.Nodes) == 0 {
   165  					continue
   166  				}
   167  
   168  				// create hash of service; uint64
   169  				h, err := hash.Hash(srv.Nodes[0], nil)
   170  				if err != nil {
   171  					continue
   172  				}
   173  
   174  				// save the info
   175  				e.Lock()
   176  				e.leases[s.Name+node.Id] = leaseID
   177  				e.register[s.Name+node.Id] = h
   178  				e.Unlock()
   179  
   180  				break
   181  			}
   182  		}
   183  	}
   184  
   185  	var leaseNotFound bool
   186  
   187  	// renew the lease if it exists
   188  	if leaseID > 0 {
   189  		log.Tracef("Renewing existing lease for %s %d", s.Name, leaseID)
   190  		if _, err := e.client.KeepAliveOnce(context.TODO(), leaseID); err != nil {
   191  			if err != rpctypes.ErrLeaseNotFound {
   192  				return err
   193  			}
   194  
   195  			log.Tracef("Lease not found for %s %d", s.Name, leaseID)
   196  			// lease not found do register
   197  			leaseNotFound = true
   198  		}
   199  	}
   200  
   201  	// create hash of service; uint64
   202  	h, err := hash.Hash(node, nil)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	// get existing hash for the service node
   208  	e.Lock()
   209  	v, ok := e.register[s.Name+node.Id]
   210  	e.Unlock()
   211  
   212  	// the service is unchanged, skip registering
   213  	if ok && v == h && !leaseNotFound {
   214  		log.Tracef("Service %s node %s unchanged skipping registration", s.Name, node.Id)
   215  		return nil
   216  	}
   217  
   218  	service := &registry.Service{
   219  		Name:      s.Name,
   220  		Version:   s.Version,
   221  		Metadata:  s.Metadata,
   222  		Endpoints: s.Endpoints,
   223  		Nodes:     []*registry.Node{node},
   224  	}
   225  
   226  	var options registry.RegisterOptions
   227  	for _, o := range opts {
   228  		o(&options)
   229  	}
   230  
   231  	ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
   232  	defer cancel()
   233  
   234  	var lgr *clientv3.LeaseGrantResponse
   235  	if options.TTL.Seconds() > 0 {
   236  		// get a lease used to expire keys since we have a ttl
   237  		lgr, err = e.client.Grant(ctx, int64(options.TTL.Seconds()))
   238  		if err != nil {
   239  			return err
   240  		}
   241  	}
   242  
   243  	log.Tracef("Registering %s id %s with lease %v and ttl %v", service.Name, node.Id, lgr, options.TTL)
   244  	// create an entry for the node
   245  	if lgr != nil {
   246  		_, err = e.client.Put(ctx, nodePath(service.Name, node.Id), encode(service), clientv3.WithLease(lgr.ID))
   247  	} else {
   248  		_, err = e.client.Put(ctx, nodePath(service.Name, node.Id), encode(service))
   249  	}
   250  	if err != nil {
   251  		return err
   252  	}
   253  
   254  	e.Lock()
   255  	// save our hash of the service
   256  	e.register[s.Name+node.Id] = h
   257  	// save our leaseID of the service
   258  	if lgr != nil {
   259  		e.leases[s.Name+node.Id] = lgr.ID
   260  	}
   261  	e.Unlock()
   262  
   263  	return nil
   264  }
   265  
   266  func (e *etcdRegistry) Deregister(s *registry.Service) error {
   267  	if len(s.Nodes) == 0 {
   268  		return errors.New("Require at least one node")
   269  	}
   270  
   271  	for _, node := range s.Nodes {
   272  		e.Lock()
   273  		// delete our hash of the service
   274  		delete(e.register, s.Name+node.Id)
   275  		// delete our lease of the service
   276  		delete(e.leases, s.Name+node.Id)
   277  		e.Unlock()
   278  
   279  		ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
   280  		defer cancel()
   281  
   282  		log.Tracef("Deregistering %s id %s", s.Name, node.Id)
   283  		_, err := e.client.Delete(ctx, nodePath(s.Name, node.Id))
   284  		if err != nil {
   285  			return err
   286  		}
   287  	}
   288  
   289  	return nil
   290  }
   291  
   292  func (e *etcdRegistry) Register(s *registry.Service, opts ...registry.RegisterOption) error {
   293  	if len(s.Nodes) == 0 {
   294  		return errors.New("Require at least one node")
   295  	}
   296  
   297  	var gerr error
   298  
   299  	// register each node individually
   300  	for _, node := range s.Nodes {
   301  		err := e.registerNode(s, node, opts...)
   302  		if err != nil {
   303  			gerr = err
   304  		}
   305  	}
   306  
   307  	return gerr
   308  }
   309  
   310  func (e *etcdRegistry) GetService(name string) ([]*registry.Service, error) {
   311  	ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
   312  	defer cancel()
   313  
   314  	rsp, err := e.client.Get(ctx, servicePath(name)+"/", clientv3.WithPrefix(), clientv3.WithSerializable())
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  
   319  	if len(rsp.Kvs) == 0 {
   320  		return nil, registry.ErrNotFound
   321  	}
   322  
   323  	serviceMap := map[string]*registry.Service{}
   324  
   325  	for _, n := range rsp.Kvs {
   326  		if sn := decode(n.Value); sn != nil {
   327  			s, ok := serviceMap[sn.Version]
   328  			if !ok {
   329  				s = &registry.Service{
   330  					Name:      sn.Name,
   331  					Version:   sn.Version,
   332  					Metadata:  sn.Metadata,
   333  					Endpoints: sn.Endpoints,
   334  				}
   335  				serviceMap[s.Version] = s
   336  			}
   337  
   338  			s.Nodes = append(s.Nodes, sn.Nodes...)
   339  		}
   340  	}
   341  
   342  	services := make([]*registry.Service, 0, len(serviceMap))
   343  	for _, service := range serviceMap {
   344  		services = append(services, service)
   345  	}
   346  
   347  	return services, nil
   348  }
   349  
   350  func (e *etcdRegistry) ListServices() ([]*registry.Service, error) {
   351  	versions := make(map[string]*registry.Service)
   352  
   353  	ctx, cancel := context.WithTimeout(context.Background(), e.options.Timeout)
   354  	defer cancel()
   355  
   356  	rsp, err := e.client.Get(ctx, prefix, clientv3.WithPrefix(), clientv3.WithSerializable())
   357  	if err != nil {
   358  		return nil, err
   359  	}
   360  
   361  	if len(rsp.Kvs) == 0 {
   362  		return []*registry.Service{}, nil
   363  	}
   364  
   365  	for _, n := range rsp.Kvs {
   366  		sn := decode(n.Value)
   367  		if sn == nil {
   368  			continue
   369  		}
   370  		v, ok := versions[sn.Name+sn.Version]
   371  		if !ok {
   372  			versions[sn.Name+sn.Version] = sn
   373  			continue
   374  		}
   375  		// append to service:version nodes
   376  		v.Nodes = append(v.Nodes, sn.Nodes...)
   377  	}
   378  
   379  	services := make([]*registry.Service, 0, len(versions))
   380  	for _, service := range versions {
   381  		services = append(services, service)
   382  	}
   383  
   384  	// sort the services
   385  	sort.Slice(services, func(i, j int) bool { return services[i].Name < services[j].Name })
   386  
   387  	return services, nil
   388  }
   389  
   390  func (e *etcdRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) {
   391  	return newEtcdWatcher(e, e.options.Timeout, opts...)
   392  }
   393  
   394  func (e *etcdRegistry) String() string {
   395  	return "etcd"
   396  }