github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/registry/memory/memory.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); 2 // you may not use this file except in compliance with the License. 3 // You may obtain a copy of the License at 4 // 5 // https://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, 9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 // See the License for the specific language governing permissions and 11 // limitations under the License. 12 // 13 // Original source: github.com/micro/go-micro/v3/registry/memory/memory.go 14 15 // Package memory provides an in-memory registry 16 package memory 17 18 import ( 19 "context" 20 "sync" 21 "time" 22 23 "github.com/google/uuid" 24 "github.com/tickoalcantara12/micro/v3/service/logger" 25 "github.com/tickoalcantara12/micro/v3/service/registry" 26 ) 27 28 var ( 29 sendEventTime = 10 * time.Millisecond 30 ttlPruneTime = time.Second 31 ) 32 33 type node struct { 34 *registry.Node 35 TTL time.Duration 36 LastSeen time.Time 37 } 38 39 type record struct { 40 Name string 41 Version string 42 Metadata map[string]string 43 Nodes map[string]*node 44 Endpoints []*registry.Endpoint 45 } 46 47 type Registry struct { 48 options registry.Options 49 50 sync.RWMutex 51 // records is a KV map with domain name as the key and a services map as the value 52 records map[string]services 53 watchers map[string]*Watcher 54 } 55 56 // services is a KV map with service name as the key and a map of records as the value 57 type services map[string]map[string]*record 58 59 // NewRegistry returns an initialized in-memory registry 60 func NewRegistry(opts ...registry.Option) registry.Registry { 61 options := registry.Options{ 62 Context: context.Background(), 63 } 64 for _, o := range opts { 65 o(&options) 66 } 67 68 // records can be passed for testing purposes 69 records := getServiceRecords(options.Context) 70 if records == nil { 71 records = make(services) 72 } 73 74 reg := &Registry{ 75 options: options, 76 records: map[string]services{registry.DefaultDomain: records}, 77 watchers: make(map[string]*Watcher), 78 } 79 80 go reg.ttlPrune() 81 82 return reg 83 } 84 85 func (m *Registry) ttlPrune() { 86 prune := time.NewTicker(ttlPruneTime) 87 defer prune.Stop() 88 89 for { 90 select { 91 case <-prune.C: 92 m.Lock() 93 for domain, services := range m.records { 94 for service, versions := range services { 95 for version, record := range versions { 96 for id, n := range record.Nodes { 97 if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL { 98 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 99 logger.Debugf("Registry TTL expired for node %s of service %s", n.Id, service) 100 } 101 delete(m.records[domain][service][version].Nodes, id) 102 } 103 } 104 105 // if there are no nodes then delete the version 106 if len(m.records[domain][service][version].Nodes) == 0 { 107 delete(m.records[domain][service], version) 108 } 109 } 110 111 // if there are no versions left delete the service 112 if len(m.records[domain][service]) == 0 { 113 delete(m.records[domain], service) 114 } 115 } 116 } 117 m.Unlock() 118 } 119 } 120 } 121 122 func (m *Registry) sendEvent(r *registry.Result) { 123 m.RLock() 124 watchers := make([]*Watcher, 0, len(m.watchers)) 125 for _, w := range m.watchers { 126 watchers = append(watchers, w) 127 } 128 m.RUnlock() 129 130 for _, w := range watchers { 131 select { 132 case <-w.exit: 133 m.Lock() 134 delete(m.watchers, w.id) 135 m.Unlock() 136 default: 137 select { 138 case w.res <- r: 139 case <-time.After(sendEventTime): 140 } 141 } 142 } 143 } 144 145 func (m *Registry) Init(opts ...registry.Option) error { 146 for _, o := range opts { 147 o(&m.options) 148 } 149 150 // add services 151 m.Lock() 152 defer m.Unlock() 153 154 // get the existing services from the records 155 srvs, ok := m.records[registry.DefaultDomain] 156 if !ok { 157 srvs = make(services) 158 } 159 160 // loop through the services and if it doesn't yet exist, add it to the slice. This is used for 161 // testing purposes. 162 for name, record := range getServiceRecords(m.options.Context) { 163 if _, ok := srvs[name]; !ok { 164 srvs[name] = record 165 continue 166 } 167 168 for version, r := range record { 169 if _, ok := srvs[name][version]; !ok { 170 srvs[name][version] = r 171 continue 172 } 173 } 174 } 175 176 // set the services in the registry 177 m.records[registry.DefaultDomain] = srvs 178 return nil 179 } 180 181 func (m *Registry) Options() registry.Options { 182 return m.options 183 } 184 185 func (m *Registry) Register(s *registry.Service, opts ...registry.RegisterOption) error { 186 m.Lock() 187 defer m.Unlock() 188 189 // parse the options, fallback to the default domain 190 var options registry.RegisterOptions 191 for _, o := range opts { 192 o(&options) 193 } 194 if len(options.Domain) == 0 { 195 options.Domain = registry.DefaultDomain 196 } 197 198 // get the services for this domain from the registry 199 srvs, ok := m.records[options.Domain] 200 if !ok { 201 srvs = make(services) 202 } 203 204 // domain is set in metadata so it can be passed to watchers 205 if s.Metadata == nil { 206 s.Metadata = map[string]string{"domain": options.Domain} 207 } else { 208 s.Metadata["domain"] = options.Domain 209 } 210 211 // ensure the service name exists 212 r := serviceToRecord(s, options.TTL) 213 if _, ok := srvs[s.Name]; !ok { 214 srvs[s.Name] = make(map[string]*record) 215 } 216 217 if _, ok := srvs[s.Name][s.Version]; !ok { 218 srvs[s.Name][s.Version] = r 219 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 220 logger.Debugf("Registry added new service: %s, version: %s", s.Name, s.Version) 221 } 222 m.records[options.Domain] = srvs 223 go m.sendEvent(®istry.Result{Action: "create", Service: s}) 224 } 225 226 var addedNodes bool 227 228 for _, n := range s.Nodes { 229 // check if already exists 230 if _, ok := srvs[s.Name][s.Version].Nodes[n.Id]; ok { 231 continue 232 } 233 234 metadata := make(map[string]string) 235 236 // make copy of metadata 237 for k, v := range n.Metadata { 238 metadata[k] = v 239 } 240 241 // set the domain 242 metadata["domain"] = options.Domain 243 244 // add the node 245 srvs[s.Name][s.Version].Nodes[n.Id] = &node{ 246 Node: ®istry.Node{ 247 Id: n.Id, 248 Address: n.Address, 249 Metadata: metadata, 250 }, 251 TTL: options.TTL, 252 LastSeen: time.Now(), 253 } 254 255 addedNodes = true 256 } 257 258 if addedNodes { 259 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 260 logger.Debugf("Registry added new node to service: %s, version: %s", s.Name, s.Version) 261 } 262 go m.sendEvent(®istry.Result{Action: "update", Service: s}) 263 } else { 264 // refresh TTL and timestamp 265 for _, n := range s.Nodes { 266 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 267 logger.Debugf("Updated registration for service: %s, version: %s", s.Name, s.Version) 268 } 269 srvs[s.Name][s.Version].Nodes[n.Id].TTL = options.TTL 270 srvs[s.Name][s.Version].Nodes[n.Id].LastSeen = time.Now() 271 } 272 } 273 274 m.records[options.Domain] = srvs 275 return nil 276 } 277 278 func (m *Registry) Deregister(s *registry.Service, opts ...registry.DeregisterOption) error { 279 m.Lock() 280 defer m.Unlock() 281 282 // parse the options, fallback to the default domain 283 var options registry.DeregisterOptions 284 for _, o := range opts { 285 o(&options) 286 } 287 if len(options.Domain) == 0 { 288 options.Domain = registry.DefaultDomain 289 } 290 291 // domain is set in metadata so it can be passed to watchers 292 if s.Metadata == nil { 293 s.Metadata = map[string]string{"domain": options.Domain} 294 } else { 295 s.Metadata["domain"] = options.Domain 296 } 297 298 // if the domain doesn't exist, there is nothing to deregister 299 services, ok := m.records[options.Domain] 300 if !ok { 301 return nil 302 } 303 304 // if no services with this name and version exist, there is nothing to deregister 305 versions, ok := services[s.Name] 306 if !ok { 307 return nil 308 } 309 310 version, ok := versions[s.Version] 311 if !ok { 312 return nil 313 } 314 315 // deregister all of the service nodes from this version 316 for _, n := range s.Nodes { 317 if _, ok := version.Nodes[n.Id]; ok { 318 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 319 logger.Debugf("Registry removed node from service: %s, version: %s", s.Name, s.Version) 320 } 321 delete(version.Nodes, n.Id) 322 } 323 } 324 325 // if the nodes not empty, we replace the version in the store and exist, the rest of the logic 326 // is cleanup 327 if len(version.Nodes) > 0 { 328 m.records[options.Domain][s.Name][s.Version] = version 329 go m.sendEvent(®istry.Result{Action: "update", Service: s}) 330 return nil 331 } 332 333 // if this version was the only version of the service, we can remove the whole service from the 334 // registry and exit 335 if len(versions) == 1 { 336 delete(m.records[options.Domain], s.Name) 337 go m.sendEvent(®istry.Result{Action: "delete", Service: s}) 338 339 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 340 logger.Debugf("Registry removed service: %s", s.Name) 341 } 342 return nil 343 } 344 345 // there are other versions of the service running, so only remove this version of it 346 delete(m.records[options.Domain][s.Name], s.Version) 347 go m.sendEvent(®istry.Result{Action: "delete", Service: s}) 348 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 349 logger.Debugf("Registry removed service: %s, version: %s", s.Name, s.Version) 350 } 351 352 return nil 353 } 354 355 func (m *Registry) GetService(name string, opts ...registry.GetOption) ([]*registry.Service, error) { 356 // parse the options, fallback to the default domain 357 var options registry.GetOptions 358 for _, o := range opts { 359 o(&options) 360 } 361 if len(options.Domain) == 0 { 362 options.Domain = registry.DefaultDomain 363 } 364 365 // if it's a wildcard domain, return from all domains 366 if options.Domain == registry.WildcardDomain { 367 m.RLock() 368 recs := m.records 369 m.RUnlock() 370 371 var services []*registry.Service 372 373 for domain := range recs { 374 srvs, err := m.GetService(name, append(opts, registry.GetDomain(domain))...) 375 if err == registry.ErrNotFound { 376 continue 377 } else if err != nil { 378 return nil, err 379 } 380 services = append(services, srvs...) 381 } 382 383 if len(services) == 0 { 384 return nil, registry.ErrNotFound 385 } 386 return services, nil 387 } 388 389 m.RLock() 390 defer m.RUnlock() 391 392 // check the domain exists 393 services, ok := m.records[options.Domain] 394 if !ok { 395 return nil, registry.ErrNotFound 396 } 397 398 // check the service exists 399 versions, ok := services[name] 400 if !ok || len(versions) == 0 { 401 return nil, registry.ErrNotFound 402 } 403 404 // serialize the response 405 result := make([]*registry.Service, len(versions)) 406 407 var i int 408 409 for _, r := range versions { 410 result[i] = recordToService(r, options.Domain) 411 i++ 412 } 413 414 return result, nil 415 } 416 417 func (m *Registry) ListServices(opts ...registry.ListOption) ([]*registry.Service, error) { 418 // parse the options, fallback to the default domain 419 var options registry.ListOptions 420 for _, o := range opts { 421 o(&options) 422 } 423 if len(options.Domain) == 0 { 424 options.Domain = registry.DefaultDomain 425 } 426 427 // if it's a wildcard domain, list from all domains 428 if options.Domain == registry.WildcardDomain { 429 m.RLock() 430 recs := m.records 431 m.RUnlock() 432 433 var services []*registry.Service 434 435 for domain := range recs { 436 srvs, err := m.ListServices(append(opts, registry.ListDomain(domain))...) 437 if err != nil { 438 return nil, err 439 } 440 services = append(services, srvs...) 441 } 442 443 return services, nil 444 } 445 446 m.RLock() 447 defer m.RUnlock() 448 449 // ensure the domain exists 450 services, ok := m.records[options.Domain] 451 if !ok { 452 return make([]*registry.Service, 0), nil 453 } 454 455 // serialize the result, each version counts as an individual service 456 var result []*registry.Service 457 458 for _, service := range services { 459 for _, version := range service { 460 result = append(result, recordToService(version, options.Domain)) 461 } 462 } 463 464 return result, nil 465 } 466 467 func (m *Registry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) { 468 // parse the options, fallback to the default domain 469 var wo registry.WatchOptions 470 for _, o := range opts { 471 o(&wo) 472 } 473 if len(wo.Domain) == 0 { 474 wo.Domain = registry.DefaultDomain 475 } 476 477 // construct the watcher 478 w := &Watcher{ 479 exit: make(chan bool), 480 res: make(chan *registry.Result), 481 id: uuid.New().String(), 482 wo: wo, 483 } 484 485 m.Lock() 486 m.watchers[w.id] = w 487 m.Unlock() 488 489 return w, nil 490 } 491 492 func (m *Registry) String() string { 493 return "memory" 494 }