Go-micro: Can we provide service isolation options between networks.

Created on 8 Nov 2019  路  5Comments  路  Source: asim/go-micro

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.

Network enhancement question

All 5 comments

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
}

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.

Was this page helpful?
0 / 5 - 0 ratings