github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/router/registry/registry.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/tickoalcantara12/micro/v3/router/registry/registry.go 14 15 package registry 16 17 import ( 18 "fmt" 19 "strings" 20 "sync" 21 "time" 22 23 "github.com/tickoalcantara12/micro/v3/service/logger" 24 "github.com/tickoalcantara12/micro/v3/service/registry" 25 "github.com/tickoalcantara12/micro/v3/service/router" 26 ) 27 28 var ( 29 // RefreshInterval is the time at which we completely refresh the table 30 RefreshInterval = time.Second * 30 31 ) 32 33 // router implements router interface 34 type registryRouter struct { 35 sync.RWMutex 36 37 running bool 38 table *table 39 options router.Options 40 exit chan bool 41 initChan chan bool 42 } 43 44 // NewRouter creates new router and returns it 45 func NewRouter(opts ...router.Option) router.Router { 46 // get default options 47 options := router.DefaultOptions() 48 49 // apply requested options 50 for _, o := range opts { 51 o(&options) 52 } 53 54 // construct the router 55 r := ®istryRouter{ 56 options: options, 57 initChan: make(chan bool, 1), 58 table: newTable(), 59 exit: make(chan bool), 60 } 61 62 // initialise the router 63 r.Init() 64 65 return r 66 } 67 68 // Init initializes router with given options 69 func (r *registryRouter) Init(opts ...router.Option) error { 70 r.Lock() 71 for _, o := range opts { 72 o(&r.options) 73 } 74 r.Unlock() 75 76 // add default gateway into routing table 77 if r.options.Gateway != "" { 78 // note, the only non-default value is the gateway 79 route := router.Route{ 80 Service: "*", 81 Address: "*", 82 Gateway: r.options.Gateway, 83 Network: "*", 84 Router: r.options.Id, 85 Link: router.DefaultLink, 86 Metric: router.DefaultMetric, 87 } 88 if err := r.table.Create(route); err != nil && err != router.ErrDuplicateRoute { 89 return fmt.Errorf("failed adding default gateway route: %s", err) 90 } 91 } 92 93 // only cache if told to do so 94 if r.options.Cache { 95 r.run() 96 } 97 98 // push a message to the init chan so the watchers 99 // can reset in the case the registry was changed 100 go func() { 101 select { 102 case r.initChan <- true: 103 default: 104 } 105 }() 106 107 return nil 108 } 109 110 // Options returns router options 111 func (r *registryRouter) Options() router.Options { 112 r.RLock() 113 defer r.RUnlock() 114 115 options := r.options 116 117 return options 118 } 119 120 // Table returns routing table 121 func (r *registryRouter) Table() router.Table { 122 r.Lock() 123 defer r.Unlock() 124 return r.table 125 } 126 127 func getDomain(srv *registry.Service) string { 128 // check the service metadata for domain 129 // TODO: domain as Domain field in registry? 130 if srv.Metadata != nil && len(srv.Metadata["domain"]) > 0 { 131 return srv.Metadata["domain"] 132 } else if len(srv.Nodes) > 0 && srv.Nodes[0].Metadata != nil { 133 return srv.Nodes[0].Metadata["domain"] 134 } 135 136 // otherwise return wildcard 137 // TODO: return GlobalDomain or PublicDomain 138 return registry.DefaultDomain 139 } 140 141 // manageRoute applies action on a given route 142 func (r *registryRouter) manageRoute(route router.Route, action string) error { 143 switch action { 144 case "create": 145 if err := r.table.Create(route); err != nil && err != router.ErrDuplicateRoute { 146 return fmt.Errorf("failed adding route for service %s: %s", route.Service, err) 147 } 148 case "delete": 149 if err := r.table.Delete(route); err != nil && err != router.ErrRouteNotFound { 150 return fmt.Errorf("failed deleting route for service %s: %s", route.Service, err) 151 } 152 case "update": 153 if err := r.table.Update(route); err != nil { 154 return fmt.Errorf("failed updating route for service %s: %s", route.Service, err) 155 } 156 default: 157 return fmt.Errorf("failed to manage route for service %s: unknown action %s", route.Service, action) 158 } 159 160 return nil 161 } 162 163 // createRoutes turns a service into a list routes basically converting nodes to routes 164 func (r *registryRouter) createRoutes(service *registry.Service, network string) []router.Route { 165 var routes []router.Route 166 167 for _, node := range service.Nodes { 168 routes = append(routes, router.Route{ 169 Service: service.Name, 170 Address: node.Address, 171 Gateway: "", 172 Network: network, 173 Router: r.options.Id, 174 Link: router.DefaultLink, 175 Metric: router.DefaultMetric, 176 Metadata: node.Metadata, 177 }) 178 } 179 180 return routes 181 } 182 183 // manageServiceRoutes applies action to all routes of the service. 184 // It returns error of the action fails with error. 185 func (r *registryRouter) manageRoutes(service *registry.Service, action, network string) error { 186 // action is the routing table action 187 action = strings.ToLower(action) 188 189 // create a set of routes from the service 190 routes := r.createRoutes(service, network) 191 192 // if its a delete action and there's no nodes 193 // it means we need to wipe out all the routes 194 // for that service 195 if action == "delete" && len(routes) == 0 { 196 // delete the service entirely 197 r.table.deleteService(service.Name, network) 198 return nil 199 } 200 201 // create the routes in the table 202 for _, route := range routes { 203 logger.Tracef("Creating route %v domain: %v", route, network) 204 if err := r.manageRoute(route, action); err != nil { 205 return err 206 } 207 } 208 209 return nil 210 } 211 212 // loadRoutes applies action to all routes of each service found in the registry. 213 // It returns error if either the services failed to be listed or the routing table action fails. 214 func (r *registryRouter) loadRoutes(name, domain string) error { 215 var services []*registry.Service 216 var err error 217 218 if len(domain) == 0 { 219 domain = registry.WildcardDomain 220 } 221 222 if len(name) > 0 { 223 services, err = r.options.Registry.GetService(name, registry.GetDomain(domain)) 224 } else { 225 services, err = r.options.Registry.ListServices(registry.ListDomain(domain)) 226 } 227 228 if err != nil { 229 return fmt.Errorf("failed listing services: %v", err) 230 } 231 232 // delete the services first 233 for _, service := range services { 234 // get the services domain from metadata. Fallback to wildcard. 235 domain := getDomain(service) 236 237 // delete the existing service 238 r.table.deleteService(service.Name, domain) 239 } 240 241 // add each service version as a separate set of routes 242 for _, service := range services { 243 // get the services domain from metadata. Fallback to wildcard. 244 domain := getDomain(service) 245 246 // create the routes 247 routes := r.createRoutes(service, domain) 248 249 // if the routes exist save them 250 if len(routes) > 0 { 251 logger.Tracef("Creating routes for service %v domain: %v", service, domain) 252 for _, rt := range routes { 253 err := r.table.Create(rt) 254 255 // update the route to prevent it from expiring 256 if err == router.ErrDuplicateRoute { 257 err = r.table.Update(rt) 258 } 259 260 if err != nil { 261 logger.Errorf("Error creating route for service %v in domain %v: %v", service, domain, err) 262 } 263 } 264 continue 265 } 266 267 // otherwise get all the service info 268 269 // get the service to retrieve all its info 270 srvs, err := r.options.Registry.GetService(service.Name, registry.GetDomain(domain)) 271 if err != nil { 272 logger.Tracef("Failed to get service %s domain: %s", service.Name, domain) 273 continue 274 } 275 276 // manage the routes for all returned services 277 for _, srv := range srvs { 278 routes := r.createRoutes(srv, domain) 279 280 if len(routes) > 0 { 281 logger.Tracef("Creating routes for service %v domain: %v", srv, domain) 282 for _, rt := range routes { 283 err := r.table.Create(rt) 284 285 // update the route to prevent it from expiring 286 if err == router.ErrDuplicateRoute { 287 err = r.table.Update(rt) 288 } 289 290 if err != nil { 291 logger.Errorf("Error creating route for service %v in domain %v: %v", service, domain, err) 292 } 293 } 294 } 295 } 296 } 297 298 return nil 299 } 300 301 // Close the router 302 func (r *registryRouter) Close() error { 303 r.Lock() 304 defer r.Unlock() 305 306 select { 307 case <-r.exit: 308 return nil 309 default: 310 if !r.running { 311 return nil 312 } 313 close(r.exit) 314 315 } 316 317 r.running = false 318 return nil 319 } 320 321 // lookup retrieves all the routes for a given service and creates them in the routing table 322 func (r *registryRouter) Lookup(service string, opts ...router.LookupOption) ([]router.Route, error) { 323 q := router.NewLookup(opts...) 324 325 // if we find the routes filter and return them 326 routes, err := r.table.Read(router.ReadService(service)) 327 if err == nil { 328 routes = router.Filter(routes, q) 329 if len(routes) == 0 { 330 return nil, router.ErrRouteNotFound 331 } 332 return routes, nil 333 } 334 335 // lookup the route 336 logger.Tracef("Fetching route for %s domain: %v", service, registry.WildcardDomain) 337 338 services, err := r.options.Registry.GetService(service, registry.GetDomain(registry.WildcardDomain)) 339 if err == registry.ErrNotFound { 340 logger.Tracef("Failed to find route for %s", service) 341 return nil, router.ErrRouteNotFound 342 } else if err != nil { 343 logger.Tracef("Failed to find route for %s: %v", service, err) 344 return nil, fmt.Errorf("failed getting services: %v", err) 345 } 346 347 for _, srv := range services { 348 domain := getDomain(srv) 349 // TODO: should we continue to send the event indicating we created a route? 350 // lookup is only called in the query path so probably not 351 routes = append(routes, r.createRoutes(srv, domain)...) 352 } 353 354 // if we're supposed to cache then save the routes 355 if r.options.Cache { 356 for _, route := range routes { 357 r.table.Create(route) 358 } 359 } 360 361 routes = router.Filter(routes, q) 362 if len(routes) == 0 { 363 return nil, router.ErrRouteNotFound 364 } 365 return routes, nil 366 } 367 368 // watchRegistry watches registry and updates routing table based on the received events. 369 // It returns error if either the registry watcher fails with error or if the routing table update fails. 370 func (r *registryRouter) watchRegistry(w registry.Watcher) error { 371 exit := make(chan bool) 372 373 defer func() { 374 close(exit) 375 }() 376 377 go func() { 378 defer w.Stop() 379 380 select { 381 case <-exit: 382 return 383 case <-r.initChan: 384 return 385 case <-r.exit: 386 return 387 } 388 }() 389 390 for { 391 // get the next service 392 res, err := w.Next() 393 if err != nil { 394 if err != registry.ErrWatcherStopped { 395 return err 396 } 397 break 398 } 399 400 // don't process nil entries 401 if res.Service == nil { 402 logger.Trace("Received a nil service") 403 continue 404 } 405 406 logger.Tracef("Router dealing with next event %s %+v\n", res.Action, res.Service) 407 408 // we only use the registry notifications as events 409 // then go on to actually query it for the full list 410 411 // get the services domain from metadata. Fallback to wildcard. 412 domain := getDomain(res.Service) 413 414 // load routes for this service 415 if err := r.loadRoutes(res.Service.Name, domain); err != nil { 416 return err 417 } 418 } 419 420 return nil 421 } 422 423 // start the router. Should be called under lock. 424 func (r *registryRouter) run() error { 425 if r.running { 426 return nil 427 } 428 429 // set running 430 r.running = true 431 432 // create a refresh notify channel 433 refresh := make(chan bool, 1) 434 435 // fires the refresh for loading routes 436 refreshRoutes := func() { 437 select { 438 case refresh <- true: 439 default: 440 } 441 } 442 443 // refresh all the routes in the event of a failure watching the registry 444 go func() { 445 var lastRefresh time.Time 446 447 // load a refresh 448 refreshRoutes() 449 450 for { 451 select { 452 case <-r.exit: 453 return 454 case <-refresh: 455 // load new routes 456 if err := r.loadRoutes("", ""); err != nil { 457 logger.Debugf("failed refreshing registry routes: %s", err) 458 // in this don't prune 459 continue 460 } 461 462 // first time so nothing to prune 463 if !lastRefresh.IsZero() { 464 // prune any routes since last refresh since we've 465 // updated basically everything we care about 466 r.table.pruneRoutes(time.Since(lastRefresh)) 467 } 468 469 // update the refresh time 470 lastRefresh = time.Now() 471 case <-time.After(RefreshInterval): 472 refreshRoutes() 473 } 474 } 475 }() 476 477 go func() { 478 for { 479 select { 480 case <-r.exit: 481 return 482 default: 483 logger.Tracef("Router starting registry watch") 484 w, err := r.options.Registry.Watch(registry.WatchDomain(registry.WildcardDomain)) 485 if err != nil { 486 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 487 logger.Debugf("failed creating registry watcher: %v", err) 488 } 489 time.Sleep(time.Second) 490 // in the event of an error reload routes 491 refreshRoutes() 492 continue 493 } 494 495 // watchRegistry calls stop when it's done 496 if err := r.watchRegistry(w); err != nil { 497 if logger.V(logger.DebugLevel, logger.DefaultLogger) { 498 logger.Debugf("Error watching the registry: %v", err) 499 } 500 time.Sleep(time.Second) 501 // in the event of an error reload routes 502 refreshRoutes() 503 } 504 } 505 } 506 }() 507 508 return nil 509 } 510 511 // Watch routes 512 func (r *registryRouter) Watch(opts ...router.WatchOption) (router.Watcher, error) { 513 return r.table.Watch(opts...) 514 } 515 516 // String prints debugging information about router 517 func (r *registryRouter) String() string { 518 return "registry" 519 }