// Forget removes a buddy from the buddy list func Forget(dependencies *map[string]time.Time, router *ribbon.Router, msg gotocol.Message) { microservice := msg.Intention // message body is buddy name to forget if router.Named(microservice) != nil { // an existing buddy to forget // forget how to talk to this buddy (*dependencies)[names.Service(microservice)] = msg.Sent // remember when we were told to forget this service router.Remove(microservice) } }
func Put(msg gotocol.Message, name string, listener chan gotocol.Message, requestor *map[string]gotocol.Routetype, router *ribbon.Router) { // pass on request to a random service - client send c := router.Random() if c == nil { return } outmsg := gotocol.Message{gotocol.Put, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention} flow.AnnotateSend(outmsg, name) outmsg.GoSend(c) }
func GetRequest(msg gotocol.Message, name string, listener chan gotocol.Message, requestor *map[string]gotocol.Routetype, router *ribbon.Router) { // pass on request to a random service - client send c := router.Random() if c == nil { return } outmsg := gotocol.Message{gotocol.GetRequest, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention} flow.AnnotateSend(outmsg, name) (*requestor)[outmsg.Ctx.Route()] = msg.Route() // remember where to respond to when this span comes back outmsg.GoSend(c) }
// NameDrop updates local buddy list func NameDrop(dependencies *map[string]time.Time, router *ribbon.Router, msg gotocol.Message, name string, listener chan gotocol.Message, eureka map[string]chan gotocol.Message, crosszone ...bool) { if msg.ResponseChan == nil { // dependency by service name, needs to be looked up in eureka (*dependencies)[msg.Intention] = msg.Sent // remember it for later for _, ch := range eureka { //log.Println(name + " looking up " + msg.Intention) gotocol.Send(ch, gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NilContext, msg.Intention}) } } else { // update dependency with full name and listener channel microservice := msg.Intention // message body is buddy name if len(crosszone) > 0 || names.Zone(name) == names.Zone(microservice) { if microservice != name && router.Named(microservice) == nil { // don't talk to myself or record duplicates // remember how to talk to this buddy router.Add(microservice, msg.ResponseChan, msg.Sent) // message channel is buddy's listener (*dependencies)[names.Service(microservice)] = msg.Sent for _, ch := range eureka { // tell just one of the service registries I have a new buddy to talk to so it doesn't get logged more than once gotocol.Send(ch, gotocol.Message{gotocol.Inform, listener, time.Now(), gotocol.NilContext, name + " " + microservice}) return } } } } }
// Start staash, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { microservices := ribbon.MakeRouter() // outbound routes var caches, stores, volumes, cass, staash *ribbon.Router // subsets of the router dependencies := make(map[string]time.Time) // dependent service names and time last updated var parent chan gotocol.Message // remember how to talk back to creator requestor := make(map[string]gotocol.Routetype) // remember where requests came from when responding var name string // remember my name eureka := make(map[string]chan gotocol.Message, 1) // service registry hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) for { select { case msg := <-listener: flow.Instrument(msg, name, hist) switch msg.Imposition { case gotocol.Hello: if name == "" { // if I don't have a name yet remember what I've been named parent = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name hist = collect.NewHist(name) } case gotocol.Inform: eureka[msg.Intention] = handlers.Inform(msg, name, listener) case gotocol.NameDrop: handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true) // true to setup cross zone routing caches = microservices.All(CachePkg) volumes = microservices.All(VolumePkg) stores = microservices.All(StorePkg) cass = microservices.All(PriamCassandraPkg) staash = microservices.All(StaashPkg) case gotocol.Forget: // forget a buddy handlers.Forget(&dependencies, microservices, msg) caches = microservices.All(CachePkg) volumes = microservices.All(VolumePkg) stores = microservices.All(StorePkg) cass = microservices.All(PriamCassandraPkg) staash = microservices.All(StaashPkg) case gotocol.GetRequest: // route the request on to a cache first if configured r := gotocol.PickRoute(requestor, msg) if caches.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, caches) r.State = cacheLookup } else { // route to any volumes if configured if volumes.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, volumes) r.State = volumeLookup } else { // route to any cassandra if configured if cass.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, cass) r.State = cassandraLookup } else { // route to stores if configured if stores.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, stores) r.State = storeLookup } else { // route to more staash layers if configured if staash.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, staash) r.State = staashLookup } } } } } //log.Printf("%v: %v route: %v", name, msg.Context, r) case gotocol.GetResponse: // return path from a request, resend or send payload back up using saved span context - server send r := gotocol.PickRoute(requestor, msg) if msg.Intention != "" { // we got a value, so pass it back up handlers.GetResponse(msg, name, listener, &requestor) } else { switch r.State { case cacheLookup: if volumes.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, volumes) r.State = volumeLookup break } fallthrough // no volumes so look for cassandra case volumeLookup: if cass.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, cass) r.State = cassandraLookup break } fallthrough // no cassandra so look for stores case cassandraLookup: if stores.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, stores) r.State = storeLookup break } fallthrough // no stores case storeLookup: if staash.Len() > 0 { handlers.GetRequest(msg, name, listener, &requestor, staash) r.State = staashLookup break } fallthrough // no staash case staashLookup: // ran out of options to find anything so pass empty response back up handlers.GetResponse(msg, name, listener, &requestor) case newRequest: default: } } case gotocol.Put: // duplicate the request to any cache, volumes, stores, and cassandra but only to one of each type // storage class packages sideways Replicate if configured // to get a lossy write, configure multiple stores that don't cross replicate handlers.Put(msg, name, listener, &requestor, caches) handlers.Put(msg, name, listener, &requestor, cass) handlers.Put(msg, name, listener, &requestor, staash) handlers.Put(msg, name, listener, &requestor, stores) msg.Intention = names.Instance(name) + "/" + msg.Intention // store to an instance specific volume namespace handlers.Put(msg, name, listener, &requestor, volumes) case gotocol.Goodbye: for _, ch := range eureka { // tell name service I'm not going to be here ch <- gotocol.Message{gotocol.Delete, nil, time.Now(), gotocol.NilContext, name} } gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(parent) return } case <-eurekaTicker.C: // check to see if any new dependencies have appeared for dep, _ := range dependencies { for _, ch := range eureka { ch <- gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NilContext, dep} } } } } }