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 }