github.com/annwntech/go-micro/v2@v2.9.5/router/table.go (about)

     1  package router
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/google/uuid"
     9  	"github.com/annwntech/go-micro/v2/logger"
    10  )
    11  
    12  var (
    13  	// ErrRouteNotFound is returned when no route was found in the routing table
    14  	ErrRouteNotFound = errors.New("route not found")
    15  	// ErrDuplicateRoute is returned when the route already exists
    16  	ErrDuplicateRoute = errors.New("duplicate route")
    17  )
    18  
    19  // table is an in-memory routing table
    20  type table struct {
    21  	sync.RWMutex
    22  	// routes stores service routes
    23  	routes map[string]map[uint64]Route
    24  	// watchers stores table watchers
    25  	watchers map[string]*tableWatcher
    26  }
    27  
    28  // newtable creates a new routing table and returns it
    29  func newTable(opts ...Option) *table {
    30  	return &table{
    31  		routes:   make(map[string]map[uint64]Route),
    32  		watchers: make(map[string]*tableWatcher),
    33  	}
    34  }
    35  
    36  // sendEvent sends events to all subscribed watchers
    37  func (t *table) sendEvent(e *Event) {
    38  	t.RLock()
    39  	defer t.RUnlock()
    40  
    41  	if len(e.Id) == 0 {
    42  		e.Id = uuid.New().String()
    43  	}
    44  
    45  	for _, w := range t.watchers {
    46  		select {
    47  		case w.resChan <- e:
    48  		case <-w.done:
    49  		// don't block forever
    50  		case <-time.After(time.Second):
    51  		}
    52  	}
    53  }
    54  
    55  // Create creates new route in the routing table
    56  func (t *table) Create(r Route) error {
    57  	service := r.Service
    58  	sum := r.Hash()
    59  
    60  	t.Lock()
    61  	defer t.Unlock()
    62  
    63  	// check if there are any routes in the table for the route destination
    64  	if _, ok := t.routes[service]; !ok {
    65  		t.routes[service] = make(map[uint64]Route)
    66  	}
    67  
    68  	// add new route to the table for the route destination
    69  	if _, ok := t.routes[service][sum]; !ok {
    70  		t.routes[service][sum] = r
    71  		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
    72  			logger.Debugf("Router emitting %s for route: %s", Create, r.Address)
    73  		}
    74  		go t.sendEvent(&Event{Type: Create, Timestamp: time.Now(), Route: r})
    75  		return nil
    76  	}
    77  
    78  	return ErrDuplicateRoute
    79  }
    80  
    81  // Delete deletes the route from the routing table
    82  func (t *table) Delete(r Route) error {
    83  	service := r.Service
    84  	sum := r.Hash()
    85  
    86  	t.Lock()
    87  	defer t.Unlock()
    88  
    89  	if _, ok := t.routes[service]; !ok {
    90  		return ErrRouteNotFound
    91  	}
    92  
    93  	if _, ok := t.routes[service][sum]; !ok {
    94  		return ErrRouteNotFound
    95  	}
    96  
    97  	delete(t.routes[service], sum)
    98  	if logger.V(logger.DebugLevel, logger.DefaultLogger) {
    99  		logger.Debugf("Router emitting %s for route: %s", Delete, r.Address)
   100  	}
   101  	go t.sendEvent(&Event{Type: Delete, Timestamp: time.Now(), Route: r})
   102  
   103  	return nil
   104  }
   105  
   106  // Update updates routing table with the new route
   107  func (t *table) Update(r Route) error {
   108  	service := r.Service
   109  	sum := r.Hash()
   110  
   111  	t.Lock()
   112  	defer t.Unlock()
   113  
   114  	// check if the route destination has any routes in the table
   115  	if _, ok := t.routes[service]; !ok {
   116  		t.routes[service] = make(map[uint64]Route)
   117  	}
   118  
   119  	if _, ok := t.routes[service][sum]; !ok {
   120  		t.routes[service][sum] = r
   121  		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   122  			logger.Debugf("Router emitting %s for route: %s", Update, r.Address)
   123  		}
   124  		go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r})
   125  		return nil
   126  	}
   127  
   128  	// just update the route, but dont emit Update event
   129  	t.routes[service][sum] = r
   130  
   131  	return nil
   132  }
   133  
   134  // List returns a list of all routes in the table
   135  func (t *table) List() ([]Route, error) {
   136  	t.RLock()
   137  	defer t.RUnlock()
   138  
   139  	var routes []Route
   140  	for _, rmap := range t.routes {
   141  		for _, route := range rmap {
   142  			routes = append(routes, route)
   143  		}
   144  	}
   145  
   146  	return routes, nil
   147  }
   148  
   149  // isMatch checks if the route matches given query options
   150  func isMatch(route Route, address, gateway, network, router string, strategy Strategy) bool {
   151  	// matches the values provided
   152  	match := func(a, b string) bool {
   153  		if a == "*" || a == b {
   154  			return true
   155  		}
   156  		return false
   157  	}
   158  
   159  	// a simple struct to hold our values
   160  	type compare struct {
   161  		a string
   162  		b string
   163  	}
   164  
   165  	// by default assume we are querying all routes
   166  	link := "*"
   167  	// if AdvertiseLocal change the link query accordingly
   168  	if strategy == AdvertiseLocal {
   169  		link = "local"
   170  	}
   171  
   172  	// compare the following values
   173  	values := []compare{
   174  		{gateway, route.Gateway},
   175  		{network, route.Network},
   176  		{router, route.Router},
   177  		{address, route.Address},
   178  		{link, route.Link},
   179  	}
   180  
   181  	for _, v := range values {
   182  		// attempt to match each value
   183  		if !match(v.a, v.b) {
   184  			return false
   185  		}
   186  	}
   187  
   188  	return true
   189  }
   190  
   191  // findRoutes finds all the routes for given network and router and returns them
   192  func findRoutes(routes map[uint64]Route, address, gateway, network, router string, strategy Strategy) []Route {
   193  	// routeMap stores the routes we're going to advertise
   194  	routeMap := make(map[string][]Route)
   195  
   196  	for _, route := range routes {
   197  		if isMatch(route, address, gateway, network, router, strategy) {
   198  			// add matchihg route to the routeMap
   199  			routeKey := route.Service + "@" + route.Network
   200  			// append the first found route to routeMap
   201  			_, ok := routeMap[routeKey]
   202  			if !ok {
   203  				routeMap[routeKey] = append(routeMap[routeKey], route)
   204  				continue
   205  			}
   206  
   207  			// if AdvertiseAll, keep appending
   208  			if strategy == AdvertiseAll || strategy == AdvertiseLocal {
   209  				routeMap[routeKey] = append(routeMap[routeKey], route)
   210  				continue
   211  			}
   212  
   213  			// now we're going to find the best routes
   214  			if strategy == AdvertiseBest {
   215  				// if the current optimal route metric is higher than routing table route, replace it
   216  				if len(routeMap[routeKey]) > 0 {
   217  					// NOTE: we know that when AdvertiseBest is set, we only ever have one item in current
   218  					if routeMap[routeKey][0].Metric > route.Metric {
   219  						routeMap[routeKey][0] = route
   220  						continue
   221  					}
   222  				}
   223  			}
   224  		}
   225  	}
   226  
   227  	var results []Route
   228  	for _, route := range routeMap {
   229  		results = append(results, route...)
   230  	}
   231  
   232  	return results
   233  }
   234  
   235  // Lookup queries routing table and returns all routes that match the lookup query
   236  func (t *table) Query(q ...QueryOption) ([]Route, error) {
   237  	t.RLock()
   238  	defer t.RUnlock()
   239  
   240  	// create new query options
   241  	opts := NewQuery(q...)
   242  
   243  	// create a cwslicelist of query results
   244  	results := make([]Route, 0, len(t.routes))
   245  
   246  	// if No routes are queried, return early
   247  	if opts.Strategy == AdvertiseNone {
   248  		return results, nil
   249  	}
   250  
   251  	if opts.Service != "*" {
   252  		if _, ok := t.routes[opts.Service]; !ok {
   253  			return nil, ErrRouteNotFound
   254  		}
   255  		return findRoutes(t.routes[opts.Service], opts.Address, opts.Gateway, opts.Network, opts.Router, opts.Strategy), nil
   256  	}
   257  
   258  	// search through all destinations
   259  	for _, routes := range t.routes {
   260  		results = append(results, findRoutes(routes, opts.Address, opts.Gateway, opts.Network, opts.Router, opts.Strategy)...)
   261  	}
   262  
   263  	return results, nil
   264  }
   265  
   266  // Watch returns routing table entry watcher
   267  func (t *table) Watch(opts ...WatchOption) (Watcher, error) {
   268  	// by default watch everything
   269  	wopts := WatchOptions{
   270  		Service: "*",
   271  	}
   272  
   273  	for _, o := range opts {
   274  		o(&wopts)
   275  	}
   276  
   277  	w := &tableWatcher{
   278  		id:      uuid.New().String(),
   279  		opts:    wopts,
   280  		resChan: make(chan *Event, 10),
   281  		done:    make(chan struct{}),
   282  	}
   283  
   284  	// when the watcher is stopped delete it
   285  	go func() {
   286  		<-w.done
   287  		t.Lock()
   288  		delete(t.watchers, w.id)
   289  		t.Unlock()
   290  	}()
   291  
   292  	// save the watcher
   293  	t.Lock()
   294  	t.watchers[w.id] = w
   295  	t.Unlock()
   296  
   297  	return w, nil
   298  }