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 := &registryRouter{
    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  }