DC2->DC1, DC3->DC1, now DC2 and DC3 can share service through DC1, and their communication is through DC1 network.
But some times I don't want DC2 and DC3 know each other.
We're working on something for network rules but would be interested to hear your ideas. How would this be configured?
I have a test.
router/default.go function flushRouteEvents and advertiseEvents add route.Link!="local" filter.
// advertiseEvents advertises routing table events
// It suppresses unhealthy flapping events and advertises healthy events upstream.
func (r *router) advertiseEvents() error {
// ticker to periodically scan event for advertising
ticker := time.NewTicker(AdvertiseEventsTick)
defer ticker.Stop()
// advertMap is a map of advert events
advertMap := make(map[uint64]*routeAdvert)
// routing table watcher
tableWatcher, err := r.Watch()
if err != nil {
return fmt.Errorf("failed creating routing table watcher: %v", err)
}
r.wg.Add(1)
go func() {
defer r.wg.Done()
select {
case r.errChan <- r.watchTable(tableWatcher):
case <-r.exit:
}
}()
for {
select {
case <-ticker.C:
var events []*Event
// collect all events which are not flapping
for key, advert := range advertMap {
// decay the event penalty
delta := time.Since(advert.lastUpdate).Seconds()
advert.penalty = advert.penalty * math.Exp(-delta*PenaltyDecay)
// suppress/recover the event based on its penalty level
switch {
case advert.penalty > AdvertSuppress && !advert.isSuppressed:
log.Debugf("Router supressing advert %d for route %s", key, advert.event.Route.Address)
advert.isSuppressed = true
advert.suppressTime = time.Now()
case advert.penalty < AdvertRecover && advert.isSuppressed:
log.Debugf("Router recovering advert %d for route %s", key, advert.event.Route.Address)
advert.isSuppressed = false
}
// max suppression time threshold has been reached, delete the advert
if advert.isSuppressed {
if time.Since(advert.suppressTime) > MaxSuppressTime {
delete(advertMap, key)
continue
}
}
if !advert.isSuppressed {
e := new(Event)
*e = *(advert.event)
if e.Route.Link != "local" {
continue
}
events = append(events, e)
// delete the advert from the advertMap
delete(advertMap, key)
}
}
// advertise all Update events to subscribers
if len(events) > 0 {
r.advertWg.Add(1)
log.Debugf("Router publishing %d events", len(events))
go r.publishAdvert(RouteUpdate, events)
}
case e := <-r.eventChan:
// if event is nil, continue
if e == nil {
continue
}
log.Debugf("Router processing table event %s for service %s", e.Type, e.Route.Address)
// determine the event penalty
var penalty float64
switch e.Type {
case Update:
penalty = UpdatePenalty
case Delete:
penalty = DeletePenalty
}
// check if we have already registered the route
hash := e.Route.Hash()
advert, ok := advertMap[hash]
if !ok {
advert = &routeAdvert{
event: e,
penalty: penalty,
lastUpdate: time.Now(),
}
advertMap[hash] = advert
continue
}
// override the route event only if the last event was different
if advert.event.Type != e.Type {
advert.event = e
}
// update event penalty and timestamp
advert.lastUpdate = time.Now()
advert.penalty += penalty
log.Debugf("Router advert %d for route %s event penalty: %f", hash, advert.event.Route.Address, advert.penalty)
case <-r.exit:
// first wait for the advertiser to finish
r.advertWg.Wait()
return nil
}
}
}
// flushRouteEvents returns a slice of events, one per each route in the routing table
func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) {
// list all routes
routes, err := r.table.List()
if err != nil {
return nil, fmt.Errorf("failed listing routes: %s", err)
}
if r.options.Advertise == AdvertiseAll {
// build a list of events to advertise
var events []*Event
for _, route := range routes {
if route.Link != "local" {
continue
}
event := &Event{
Type: evType,
Timestamp: time.Now(),
Route: route,
}
events = append(events, event)
}
return events, nil
}
// routeMap stores optimal routes per service
bestRoutes := make(map[string]Route)
// go through all routes found in the routing table and collapse them to optimal routes
for _, route := range routes {
routeKey := route.Service + "@" + route.Network
optimal, ok := bestRoutes[routeKey]
if !ok {
bestRoutes[routeKey] = route
continue
}
// if the current optimal route metric is higher than routing table route, replace it
if optimal.Metric > route.Metric {
bestRoutes[routeKey] = route
continue
}
// if the metrics are the same, prefer advertising your own route
if optimal.Metric == route.Metric {
if route.Router == r.options.Id {
bestRoutes[routeKey] = route
continue
}
}
}
log.Debugf("Router advertising %d best routes out of %d", len(bestRoutes), len(routes))
// build a list of events to advertise
var events []*Event
for _, route := range bestRoutes {
log.Infof("flushRouteEvents service: %v network: %v, link: %v", route.Service, route.Network, route.Link)
if route.Link != "local" {
continue
}
event := &Event{
Type: evType,
Timestamp: time.Now(),
Route: route,
}
events = append(events, event)
}
return events, nil
}
https://github.com/micro/go-micro/blob/174b01ca2954fce2a14614c057d0a3096c539392/router/default.go#L433
https://github.com/micro/go-micro/blob/174b01ca2954fce2a14614c057d0a3096c539392/router/default.go#L720
https://github.com/micro/go-micro/blob/174b01ca2954fce2a14614c057d0a3096c539392/router/default.go#L762
So what you're saying is you only want the router to broadcast its own routes rather than all the routes it sees. I think this is either a route advertisment strategy or some sort of network level filtering we need to decide on.
Our default mode is to share the entire routing table. Limiting that, is something we haven't yet addressed.
See strategy:
https://github.com/micro/go-micro/blob/master/router/router.go#L143L150
And how we apply that:
https://github.com/micro/go-micro/blob/master/router/default.go#L717
So we could potentially have AdvertiseLocal as a strategy so that nodes do not advertise the entire table.
https://github.com/micro/go-micro/pull/932
This may be the first step. We add AdvertiseLocal and AdvertiseNone. Allowing either to only advertise the local routes or to advertise no routes at all. We'll add flags/env vars also.