micro-router-register/table.go

268 lines
5.6 KiB
Go
Raw Normal View History

package registry
import (
"sync"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v3/logger"
"github.com/micro/go-micro/v3/router"
)
// table is an in-memory routing table
type table struct {
sync.RWMutex
// routes stores service routes
routes map[string]map[uint64]*route
// watchers stores table watchers
watchers map[string]*tableWatcher
}
type route struct {
route router.Route
updated time.Time
}
// newtable creates a new routing table and returns it
func newTable() *table {
return &table{
routes: make(map[string]map[uint64]*route),
watchers: make(map[string]*tableWatcher),
}
}
// pruneRoutes will prune routes older than the time specified
func (t *table) pruneRoutes(olderThan time.Duration) {
var routes []router.Route
t.Lock()
// search for all the routes
for _, routeList := range t.routes {
for _, r := range routeList {
// if any route is older than
if time.Since(r.updated).Seconds() > olderThan.Seconds() {
routes = append(routes, r.route)
}
}
}
t.Unlock()
// delete the routes we've found
for _, route := range routes {
t.Delete(route)
}
}
// deleteService removes the entire service
func (t *table) deleteService(service, network string) {
t.Lock()
2020-08-09 21:39:21 +03:00
defer t.Unlock()
routes, ok := t.routes[service]
if !ok {
return
}
// delete the routes for the service
for hash, rt := range routes {
// TODO: check if this causes a problem
// with * in the network if that is a thing
// or blank strings
if rt.route.Network != network {
continue
}
delete(routes, hash)
}
// delete the map for the service if its empty
if len(routes) == 0 {
delete(t.routes, service)
return
}
// save the routes
t.routes[service] = routes
}
// sendEvent sends events to all subscribed watchers
func (t *table) sendEvent(e *router.Event) {
t.RLock()
defer t.RUnlock()
if len(e.Id) == 0 {
e.Id = uuid.New().String()
}
for _, w := range t.watchers {
select {
case w.resChan <- e:
case <-w.done:
// don't block forever
case <-time.After(time.Second):
}
}
}
// Create creates new route in the routing table
func (t *table) Create(r router.Route) error {
service := r.Service
sum := r.Hash()
t.Lock()
defer t.Unlock()
// check if there are any routes in the table for the route destination
if _, ok := t.routes[service]; !ok {
t.routes[service] = make(map[uint64]*route)
}
// add new route to the table for the route destination
if _, ok := t.routes[service][sum]; ok {
return router.ErrDuplicateRoute
}
// create the route
t.routes[service][sum] = &route{r, time.Now()}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Router emitting %s for route: %s", router.Create, r.Address)
}
// send a route created event
go t.sendEvent(&router.Event{Type: router.Create, Timestamp: time.Now(), Route: r})
return nil
}
// Delete deletes the route from the routing table
func (t *table) Delete(r router.Route) error {
service := r.Service
sum := r.Hash()
t.Lock()
defer t.Unlock()
if _, ok := t.routes[service]; !ok {
return router.ErrRouteNotFound
}
if _, ok := t.routes[service][sum]; !ok {
return router.ErrRouteNotFound
}
// delete the route from the service
delete(t.routes[service], sum)
// delete the whole map if there are no routes left
if len(t.routes[service]) == 0 {
delete(t.routes, service)
}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Router emitting %s for route: %s", router.Delete, r.Address)
}
go t.sendEvent(&router.Event{Type: router.Delete, Timestamp: time.Now(), Route: r})
return nil
}
// Update updates routing table with the new route
func (t *table) Update(r router.Route) error {
service := r.Service
sum := r.Hash()
t.Lock()
defer t.Unlock()
// check if the route destination has any routes in the table
if _, ok := t.routes[service]; !ok {
t.routes[service] = make(map[uint64]*route)
}
if _, ok := t.routes[service][sum]; !ok {
// update the route
t.routes[service][sum] = &route{r, time.Now()}
if logger.V(logger.DebugLevel, logger.DefaultLogger) {
logger.Debugf("Router emitting %s for route: %s", router.Update, r.Address)
}
go t.sendEvent(&router.Event{Type: router.Update, Timestamp: time.Now(), Route: r})
return nil
}
// just update the route, but dont emit Update event
t.routes[service][sum] = &route{r, time.Now()}
return nil
}
// List returns a list of all routes in the table
func (t *table) List() ([]router.Route, error) {
t.RLock()
defer t.RUnlock()
var routes []router.Route
for _, rmap := range t.routes {
for _, route := range rmap {
routes = append(routes, route.route)
}
}
return routes, nil
}
// Lookup queries routing table and returns all routes that match the lookup query
func (t *table) Query(service string) ([]router.Route, error) {
t.RLock()
defer t.RUnlock()
routeMap, ok := t.routes[service]
if !ok {
return nil, router.ErrRouteNotFound
}
var routes []router.Route
for _, rt := range routeMap {
routes = append(routes, rt.route)
}
return routes, nil
}
// Watch returns routing table entry watcher
func (t *table) Watch(opts ...router.WatchOption) (router.Watcher, error) {
// by default watch everything
wopts := router.WatchOptions{
Service: "*",
}
for _, o := range opts {
o(&wopts)
}
w := &tableWatcher{
id: uuid.New().String(),
opts: wopts,
resChan: make(chan *router.Event, 10),
done: make(chan struct{}),
}
// when the watcher is stopped delete it
go func() {
<-w.done
t.Lock()
delete(t.watchers, w.id)
t.Unlock()
}()
// save the watcher
t.Lock()
t.watchers[w.id] = w
t.Unlock()
return w, nil
}