// Start staash, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := archaius.Conf.Population // starting point for how many nodes to remember // remember the channel to talk to microservices microservices := make(map[string]chan gotocol.Message, dunbar) microindex := make(map[int]chan gotocol.Message, dunbar) dependencies := make(map[string]time.Time, dunbar) // dependent services and time last updated var parent chan gotocol.Message // remember how to talk back to creator requestor := make(map[gotocol.TraceContextType]chan gotocol.Message) // remember where requests came from var name string // remember my name eureka := make(map[string]chan gotocol.Message, 1) // service registry var chatrate time.Duration hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) chatTicker := time.NewTicker(time.Hour) chatTicker.Stop() for { select { case msg := <-listener: collect.Measure(hist, time.Since(msg.Sent)) if archaius.Conf.Msglog { log.Printf("%v: %v\n", name, msg) } 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] = gotocol.InformHandler(msg, name, listener) case gotocol.NameDrop: gotocol.NameDropHandler(&dependencies, µservices, msg, name, listener, eureka) case gotocol.Forget: // forget a buddy gotocol.ForgetHandler(&dependencies, µservices, msg) case gotocol.Chat: // setup the ticker to run at the specified rate d, e := time.ParseDuration(msg.Intention) if e == nil && d >= time.Millisecond && d <= time.Hour { chatrate = d chatTicker = time.NewTicker(chatrate) } case gotocol.GetRequest: // route the request on to microservices requestor[msg.Ctx.Trace] = msg.ResponseChan // Intention body indicates which service to route to or which key to get // need to lookup service by type rather than randomly call one day if len(microservices) > 0 { if len(microindex) != len(microservices) { // rebuild index i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) span := msg.Ctx.NewSpan() flow.Update(span, name) // start a request to a random service gotocol.Message{gotocol.GetRequest, listener, time.Now(), span, msg.Intention}.GoSend(microindex[m]) } case gotocol.GetResponse: // return path from a request, send payload back up if requestor[msg.Ctx.Trace] != nil { span := msg.Ctx.NewSpan() flow.Update(span, name) gotocol.Message{gotocol.GetResponse, listener, time.Now(), span, msg.Intention}.GoSend(requestor[msg.Ctx.Trace]) delete(requestor, msg.Ctx.Trace) } case gotocol.Put: // route the request on to a random dependency if len(microservices) > 0 { if len(microindex) != len(microservices) { // rebuild index i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) span := msg.Ctx.NewSpan() flow.Update(span, name) // pass on request to a random service gotocol.Message{gotocol.Put, listener, time.Now(), span, msg.Intention}.GoSend(microindex[m]) } case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away\n", name) } 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} } } case <-chatTicker.C: if len(microservices) > 0 { if len(microservices) != len(microindex) { // rebuild index i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) // start a request to a random member of this elb gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NewTrace(), name}.GoSend(microindex[m]) } } } }
// Start priamCassandra, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := archaius.Conf.Population // starting point for how many nodes to remember // remember the channel to talk to microservices microservices := make(map[string]chan gotocol.Message, dunbar) // track the hash values owned by each node in the ring var ring ByToken dependencies := make(map[string]time.Time, dunbar) // dependent services and time last updated store := make(map[string]string, 4) // key value store store["why?"] = "because..." var parent chan gotocol.Message // remember how to talk back to creator var name string // remember my name eureka := make(map[string]chan gotocol.Message, 3*archaius.Conf.Regions) // service registry per zone and region hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) for { select { case msg := <-listener: collect.Measure(hist, time.Since(msg.Sent)) if archaius.Conf.Msglog { log.Printf("%v: %v\n", name, msg) } 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] = gotocol.InformHandler(msg, name, listener) case gotocol.NameDrop: // cross zone = true gotocol.NameDropHandler(&dependencies, µservices, msg, name, listener, eureka, true) case gotocol.Forget: // forget a buddy gotocol.ForgetHandler(&dependencies, µservices, msg) case gotocol.Chat: // Gossip setup notification of hash values for nodes, cass1:123,cass2:456 ring = RingConfig(msg.Intention) case gotocol.GetRequest: // see if the data is stored on this node i := ring.Find(ringHash(msg.Intention)) //log.Printf("%v: %v %v\n", name, i, ringHash(msg.Intention)) span := msg.Ctx.NewSpan() flow.Update(span, name) if len(ring) == 0 || ring[i].name == name { // ring is setup so only respond if this is the right place // return any stored value for this key (Cassandra READ.ONE behavior) gotocol.Message{gotocol.GetResponse, listener, time.Now(), span, store[msg.Intention]}.GoSend(msg.ResponseChan) } else { // send the message to the right place, but don't change the ResponseChan gotocol.Message{gotocol.GetRequest, msg.ResponseChan, time.Now(), span, msg.Intention}.GoSend(microservices[ring[i].name]) } case gotocol.GetResponse: // return path from a request, send payload back up, not used by priamCassandra currently case gotocol.Put: // set a key value pair and replicate globally var key, value string span := msg.Ctx.NewSpan() flow.Update(span, name) fmt.Sscanf(msg.Intention, "%s%s", &key, &value) if key != "" && value != "" { i := ring.Find(ringHash(key)) if len(ring) == 0 || ring[i].name == name { // ring is setup so only store if this is the right place store[key] = value } else { // send the message to the right place, but don't change the ResponseChan gotocol.Message{gotocol.Put, msg.ResponseChan, time.Now(), span, msg.Intention}.GoSend(microservices[ring[i].name]) } // duplicate the request on to priamCassandra nodes in each zone and one in each region for _, z := range names.OtherZones(name, archaius.Conf.ZoneNames) { // replicate request for n, c := range microservices { if names.Region(n) == names.Region(name) && names.Zone(n) == z { gotocol.Message{gotocol.Replicate, listener, time.Now(), span, msg.Intention}.GoSend(c) break // only need to send it to one node in each zone, no tokens yet } } } for _, r := range names.OtherRegions(name, archaius.Conf.RegionNames[0:archaius.Conf.Regions]) { for n, c := range microservices { if names.Region(n) == r { gotocol.Message{gotocol.Replicate, listener, time.Now(), span, msg.Intention}.GoSend(c) break // only need to send it to one node in each region, no tokens yet } } } } case gotocol.Replicate: // Replicate is only used between priamCassandra nodes // end point for a request var key, value string span := msg.Ctx.NewSpan() flow.Update(span, name) fmt.Sscanf(msg.Intention, "%s%s", &key, &value) // log.Printf("priamCassandra: %v:%v", key, value) if key != "" && value != "" { i := ring.Find(ringHash(key)) if len(ring) == 0 || ring[i].name == name { // ring is setup so only store if this is the right place store[key] = value } else { // send the message to the right place, but don't change the ResponseChan gotocol.Message{gotocol.Replicate, msg.ResponseChan, time.Now(), span, msg.Intention}.GoSend(microservices[ring[i].name]) } } // name looks like: netflixoss.us-east-1.zoneC.cassTurtle.priamCassandra.cassTurtle11 myregion := names.Region(name) //log.Printf("%v: %v\n", name, myregion) // find if this was a cross region Replicate for in, c := range microservices { // find the name matching incoming request channel to see where its coming from if c == msg.ResponseChan && myregion != names.Region(in) { // Replicate from out of region needs to be Replicated once only to other zones in this Region for _, z := range names.OtherZones(name, archaius.Conf.ZoneNames) { // replicate request for n, c := range microservices { if names.Region(n) == myregion && names.Zone(n) == z { gotocol.Message{gotocol.Replicate, listener, time.Now(), span, msg.Intention}.GoSend(c) break // only need to send it to one node in each zone, no tokens yet } } } break } } case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away, zone: %v\n", name, store["zone"]) } 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} } } } } }
// Start the denominator, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := 6 // starting point for how many nodes to remember // remember the channel to talk to microservices microservices := make(map[string]chan gotocol.Message, dunbar) microindex := make([]chan gotocol.Message, dunbar) dependencies := make(map[string]time.Time, dunbar) // dependent services and time last updated var parent chan gotocol.Message // remember how to talk back to creator var name string // remember my name hist := collect.NewHist("") // don't know name yet eureka := make(map[string]chan gotocol.Message, 3*archaius.Conf.Regions) // service registry per zone and region var chatrate time.Duration ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) chatTicker := time.NewTicker(time.Hour) chatTicker.Stop() w := 1 // counter for random messages for { select { case msg := <-listener: collect.Measure(hist, time.Since(msg.Sent)) if archaius.Conf.Msglog { log.Printf("%v: %v\n", name, msg) } 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] = gotocol.InformHandler(msg, name, listener) case gotocol.NameDrop: gotocol.NameDropHandler(&dependencies, µservices, msg, name, listener, eureka) case gotocol.Forget: // forget a buddy gotocol.ForgetHandler(&dependencies, µservices, msg) case gotocol.Chat: // setup the ticker to run at the specified rate d, e := time.ParseDuration(msg.Intention) if e == nil && d >= time.Millisecond && d <= time.Hour { chatrate = d chatTicker = time.NewTicker(chatrate) } case gotocol.GetResponse: // return path from a request, terminate and log flow.Update(msg.Ctx, name) flow.End(msg.Ctx) case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away, was chatting every %v\n", name, chatrate) } 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} } } case <-chatTicker.C: if len(microservices) > 0 { // build index if needed if len(microindex) != len(microservices) { i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) // start a request to a random member of this denominator ctx := gotocol.NewTrace() flow.Update(ctx, name) switch rand.Intn(3) { case 0: gotocol.Message{gotocol.GetRequest, listener, time.Now(), ctx, "why?"}.GoSend(microindex[m]) case 1: q := rand.Intn(w) // pick a random key that has already been put gotocol.Message{gotocol.GetRequest, listener, time.Now(), ctx, fmt.Sprintf("Why%v%v", q, q*q)}.GoSend(microindex[m]) case 2: gotocol.Message{gotocol.Put, listener, time.Now(), ctx, fmt.Sprintf("Why%v%v me", w, w*w)}.GoSend(microindex[m]) w++ // put a new key each time } } } } }