Simplified processEvents loop; Added router Announcement.

This commit is contained in:
Milos Gajdos 2019-07-09 12:46:15 +01:00
parent b82245429e
commit 265271008e
No known key found for this signature in database
GPG Key ID: 8B31058CC55DFD4F
2 changed files with 73 additions and 49 deletions

View File

@ -38,6 +38,7 @@ type router struct {
exit chan struct{} exit chan struct{}
eventChan chan *table.Event eventChan chan *table.Event
advertChan chan *Advert advertChan chan *Advert
advertWg *sync.WaitGroup
wg *sync.WaitGroup wg *sync.WaitGroup
sync.RWMutex sync.RWMutex
} }
@ -58,6 +59,7 @@ func newRouter(opts ...Option) Router {
exit: make(chan struct{}), exit: make(chan struct{}),
eventChan: make(chan *table.Event), eventChan: make(chan *table.Event),
advertChan: make(chan *Advert), advertChan: make(chan *Advert),
advertWg: &sync.WaitGroup{},
wg: &sync.WaitGroup{}, wg: &sync.WaitGroup{},
} }
} }
@ -97,7 +99,7 @@ func (r *router) Network() string {
// manageServiceRoutes manages routes for a given service. // manageServiceRoutes manages routes for a given service.
// It returns error of the routing table action fails. // It returns error of the routing table action fails.
func (r *router) manageServiceRoutes(service *registry.Service, action string, metric int) error { func (r *router) manageServiceRoutes(service *registry.Service, action string) error {
// action is the routing table action // action is the routing table action
action = strings.ToLower(action) action = strings.ToLower(action)
// take route action on each service node // take route action on each service node
@ -107,7 +109,7 @@ func (r *router) manageServiceRoutes(service *registry.Service, action string, m
Gateway: node.Address, Gateway: node.Address,
Router: r.opts.Address, Router: r.opts.Address,
Network: r.opts.Network, Network: r.opts.Network,
Metric: metric, Metric: table.DefaultLocalMetric,
} }
switch action { switch action {
case "insert", "create": case "insert", "create":
@ -127,7 +129,7 @@ func (r *router) manageServiceRoutes(service *registry.Service, action string, m
// manageRegistryRoutes manages routes for each service found in the registry. // manageRegistryRoutes manages routes for each service found in the registry.
// It returns error if either the services failed to be listed or the routing table action fails. // It returns error if either the services failed to be listed or the routing table action fails.
func (r *router) manageRegistryRoutes(reg registry.Registry, action string, metric int) error { func (r *router) manageRegistryRoutes(reg registry.Registry, action string) error {
services, err := reg.ListServices() services, err := reg.ListServices()
if err != nil { if err != nil {
return fmt.Errorf("failed listing services: %v", err) return fmt.Errorf("failed listing services: %v", err)
@ -143,7 +145,7 @@ func (r *router) manageRegistryRoutes(reg registry.Registry, action string, metr
} }
// manage the routes for all returned services // manage the routes for all returned services
for _, s := range srvs { for _, s := range srvs {
if err := r.manageServiceRoutes(s, action, metric); err != nil { if err := r.manageServiceRoutes(s, action); err != nil {
return err return err
} }
} }
@ -177,7 +179,7 @@ func (r *router) watchServices(w registry.Watcher) error {
log.Logf("r.watchServices() new service event: Action: %s Service: %v", res.Action, res.Service) log.Logf("r.watchServices() new service event: Action: %s Service: %v", res.Action, res.Service)
if err := r.manageServiceRoutes(res.Service, res.Action, table.DefaultLocalMetric); err != nil { if err := r.manageServiceRoutes(res.Service, res.Action); err != nil {
return err return err
} }
} }
@ -224,6 +226,29 @@ func (r *router) watchTable(w table.Watcher) error {
return watchErr return watchErr
} }
func (r *router) advertEvents(advType AdvertType, events []*table.Event) {
defer r.advertWg.Done()
log.Logf("r.advertEvents(): start event: %s", advType)
a := &Advert{
ID: r.ID(),
Type: advType,
Timestamp: time.Now(),
Events: events,
}
select {
case r.advertChan <- a:
log.Logf("r.advertEvents(): advertised event: %s", advType)
case <-r.exit:
log.Logf("r.advertEvents(): DONE exit")
return
}
log.Logf("r.advertEvents(): REGULAR exit")
}
// isFlapping detects if the event is flapping based on the current and previous event status. // isFlapping detects if the event is flapping based on the current and previous event status.
func isFlapping(curr, prev *table.Event) bool { func isFlapping(curr, prev *table.Event) bool {
if curr.Type == table.Update && prev.Type == table.Update { if curr.Type == table.Update && prev.Type == table.Update {
@ -259,18 +284,12 @@ func (r *router) processEvents() error {
ticker := time.NewTicker(AdvertiseTick) ticker := time.NewTicker(AdvertiseTick)
// eventMap is a map of advert events // eventMap is a map of advert events
eventMap := make(map[uint64]*updateEvent) eventMap := make(map[uint64]*updateEvent)
// lock to protect access to eventMap
mu := &sync.RWMutex{}
// waitgroup to manage advertisement goroutines
var wg sync.WaitGroup
processLoop:
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
var events []*table.Event var events []*table.Event
// collect all events which are not flapping // collect all events which are not flapping
mu.Lock()
for key, event := range eventMap { for key, event := range eventMap {
if !event.isFlapping && !event.isSuppressed { if !event.isFlapping && !event.isSuppressed {
e := new(table.Event) e := new(table.Event)
@ -280,29 +299,10 @@ processLoop:
delete(eventMap, key) delete(eventMap, key)
} }
} }
mu.Unlock()
if len(events) > 0 { if len(events) > 0 {
wg.Add(1) r.advertWg.Add(1)
go func(events []*table.Event) { go r.advertEvents(Update, events)
defer wg.Done()
log.Logf("go advertise(): start")
a := &Advert{
ID: r.ID(),
Timestamp: time.Now(),
Events: events,
}
select {
case r.advertChan <- a:
case <-r.exit:
log.Logf("go advertise(): exit")
return
}
log.Logf("go advertise(): exit")
}(events)
} }
case e := <-r.eventChan: case e := <-r.eventChan:
// event timestamp // event timestamp
@ -348,15 +348,16 @@ processLoop:
event.isFlapping = isFlapping(e, event.Event) event.isFlapping = isFlapping(e, event.Event)
} }
case <-r.exit: case <-r.exit:
break processLoop // first wait for the advertiser to finish
r.advertWg.Wait()
// close the advert channel
close(r.advertChan)
log.Logf("r.processEvents(): event processor stopped")
return nil
} }
} }
// first wait for the advertiser to finish // we probably never reach this place
wg.Wait()
// close the advert channel
close(r.advertChan)
log.Logf("r.processEvents(): event processor stopped") log.Logf("r.processEvents(): event processor stopped")
return nil return nil
@ -395,9 +396,11 @@ func (r *router) watchErrors(errChan <-chan error) {
// drain the advertise channel // drain the advertise channel
for range r.advertChan { for range r.advertChan {
} }
log.Logf("r.watchErrors(): advert channel drained")
// drain the event channel // drain the event channel
for range r.eventChan { for range r.eventChan {
} }
log.Logf("r.watchErrors(): event channel drained")
} }
log.Logf("r.watchErrors(): watchErrors exit") log.Logf("r.watchErrors(): watchErrors exit")
@ -411,10 +414,15 @@ func (r *router) Advertise() (<-chan *Advert, error) {
if r.status.Code != Running { if r.status.Code != Running {
// add all local service routes into the routing table // add all local service routes into the routing table
if err := r.manageRegistryRoutes(r.opts.Registry, "insert", table.DefaultLocalMetric); err != nil { if err := r.manageRegistryRoutes(r.opts.Registry, "insert"); err != nil {
return nil, fmt.Errorf("failed adding routes: %v", err) return nil, fmt.Errorf("failed adding routes: %s", err)
} }
log.Logf("Routing table:\n%s", r.opts.Table) log.Logf("Routing table:\n%s", r.opts.Table)
// list routing table routes to announce
routes, err := r.opts.Table.List()
if err != nil {
return nil, fmt.Errorf("failed listing routes: %s", err)
}
// add default gateway into routing table // add default gateway into routing table
if r.opts.Gateway != "" { if r.opts.Gateway != "" {
// note, the only non-default value is the gateway // note, the only non-default value is the gateway
@ -431,14 +439,15 @@ func (r *router) Advertise() (<-chan *Advert, error) {
} }
// NOTE: we only need to recreate these if the router errored or was stopped // NOTE: we only need to recreate these if the router errored or was stopped
// TODO: These probably dont need to be struct members
if r.status.Code == Error || r.status.Code == Stopped { if r.status.Code == Error || r.status.Code == Stopped {
r.exit = make(chan struct{}) r.exit = make(chan struct{})
r.eventChan = make(chan *table.Event) r.eventChan = make(chan *table.Event)
r.advertChan = make(chan *Advert) r.advertChan = make(chan *Advert)
} }
// routing table watcher which watches all routes i.e. to every destination // routing table watcher
tableWatcher, err := r.opts.Table.Watch(table.WatchDestination("*")) tableWatcher, err := r.opts.Table.Watch()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed creating routing table watcher: %v", err) return nil, fmt.Errorf("failed creating routing table watcher: %v", err)
} }
@ -478,11 +487,24 @@ func (r *router) Advertise() (<-chan *Advert, error) {
log.Logf("r.Advertise(): r.processEvents() exit") log.Logf("r.Advertise(): r.processEvents() exit")
}() }()
// watch for errors and cleanup
r.wg.Add(1) r.wg.Add(1)
go r.watchErrors(errChan) go r.watchErrors(errChan)
// TODO: send router announcement update comes here // announce yourself with all the existing routes
// the announcement update contains routes from routing table events := make([]*table.Event, len(routes))
for i, route := range routes {
event := &table.Event{
Type: table.Insert,
Timestamp: time.Now(),
Route: route,
}
events[i] = event
}
// advertise your presence
r.advertWg.Add(1)
go r.advertEvents(Announce, events)
// mark router as running and set its Error to nil // mark router as running and set its Error to nil
status := Status{ status := Status{

View File

@ -41,19 +41,19 @@ type Router interface {
// Option used by the router // Option used by the router
type Option func(*Options) type Option func(*Options)
// UpdateType is route advertisement update type // AdvertType is route advertisement type
type UpdateType int type AdvertType int
const ( const (
// Announce is advertised when the router announces itself // Announce is advertised when the router announces itself
Announce UpdateType = iota Announce AdvertType = iota
// Update advertises route updates // Update advertises route updates
Update Update
) )
// String returns string representation of update event // String returns string representation of update event
func (ut UpdateType) String() string { func (at AdvertType) String() string {
switch ut { switch at {
case Announce: case Announce:
return "ANNOUNCE" return "ANNOUNCE"
case Update: case Update:
@ -67,6 +67,8 @@ func (ut UpdateType) String() string {
type Advert struct { type Advert struct {
// ID is the router ID // ID is the router ID
ID string ID string
// Type is type of advert
Type AdvertType
// Timestamp marks the time when the update is sent // Timestamp marks the time when the update is sent
Timestamp time.Time Timestamp time.Time
// TTL is Advert TTL // TTL is Advert TTL