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 := ®istry.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 = ®istry.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 }