Example #1
0
// Start listening, all configuration and state is sent via messages
func Start(listener chan gotocol.Message) {
	// remember the channel to talk to microservices
	microservices := ribbon.MakeRouter()
	dependencies := make(map[string]time.Time)         // dependent services 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)
			case gotocol.Forget:
				// forget a buddy
				handlers.Forget(&dependencies, microservices, msg)
			case gotocol.GetRequest:
				// route the request on to microservices
				handlers.GetRequest(msg, name, listener, &requestor, microservices)
			case gotocol.GetResponse:
				// return path from a request, send payload back up using saved span context - server send
				handlers.GetResponse(msg, name, listener, &requestor)
			case gotocol.Put:
				// route the request on to a random dependency
				handlers.Put(msg, name, listener, &requestor, microservices)
			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}
				}
			}
		}
	}
}
Example #2
0
// Start the denominator, all configuration and state is sent via messages
func Start(listener chan gotocol.Message) {
	microservices := ribbon.MakeRouter()
	dependencies := make(map[string]time.Time)                                                          // dependent services and time last updated
	var parent chan gotocol.Message                                                                     // remember how to talk back to creator
	var name string                                                                                     // remember my name
	nethist := collect.NewHist("")                                                                      // don't know name yet - message network latency
	resphist := collect.NewHist("")                                                                     // response time history
	servhist := collect.NewHist("")                                                                     // service time history
	rthist := collect.NewHist("")                                                                       // round trip history
	eureka := make(map[string]chan gotocol.Message, len(archaius.Conf.ZoneNames)*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:
			flow.Instrument(msg, name, nethist)
			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
					nethist = collect.NewHist(name + "_net")
					resphist = collect.NewHist(name + "_resp")
					servhist = collect.NewHist(name + "_serv")
					rthist = collect.NewHist(name + "_rt")
				}
			case gotocol.Inform:
				eureka[msg.Intention] = handlers.Inform(msg, name, listener)
			case gotocol.NameDrop:
				handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true)
			case gotocol.Forget:
				// forget a buddy
				handlers.Forget(&dependencies, microservices, 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 response time in histograms
				flow.End(msg, resphist, servhist, rthist)
			case gotocol.Goodbye:
				if archaius.Conf.Msglog {
					log.Printf("%v: Going away, was chatting every %v\n", name, chatrate)
				}
				collect.SaveHist(nethist, name, "_net")
				collect.SaveHist(resphist, name, "_resp")
				collect.SaveHist(servhist, name, "_serv")
				collect.SaveHist(rthist, name, "_rt")
				collect.SaveAllGuesses(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:
			c := microservices.Random()
			if c != nil {
				ctx := gotocol.NewTrace()
				now := time.Now()
				var sm gotocol.Message
				switch rand.Intn(3) {
				case 0:
					sm = gotocol.Message{gotocol.GetRequest, listener, now, ctx, "why?"}
				case 1:
					q := rand.Intn(w) // pick a random key that has already been put
					sm = gotocol.Message{gotocol.GetRequest, listener, now, ctx, fmt.Sprintf("Why%v%v", q, q*q)}
				case 2:
					sm = gotocol.Message{gotocol.Put, listener, now, ctx, fmt.Sprintf("Why%v%v me", w, w*w)}
					w++ // put a new key each time
				}
				flow.AnnotateSend(sm, name) // service send logs creation time for this flow
				sm.GoSend(c)
			}
		}
	}
}
Example #3
0
// 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}
				}
			}
		}
	}
}
Example #4
0
// Start store, all configuration and state is sent via messages
func Start(listener chan gotocol.Message) {
	// remember the channel to talk to microservices
	microservices := ribbon.MakeRouter()
	dependencies := make(map[string]time.Time) // dependent services and time last updated
	store := make(map[string]string, 4)        // key value store
	store["why?"] = "because..."
	var netflixoss chan gotocol.Message                                           // remember creator and how to talk back to incoming requests
	var name string                                                               // remember my name
	eureka := make(map[string]chan gotocol.Message, len(archaius.Conf.ZoneNames)) // service registry per zone
	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
					netflixoss = 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: // cross zone = true
				handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true)
			case gotocol.Forget:
				// forget a buddy
				handlers.Forget(&dependencies, microservices, msg)
			case gotocol.GetRequest:
				// return any stored value for this key
				outmsg := gotocol.Message{gotocol.GetResponse, listener, time.Now(), msg.Ctx, store[msg.Intention]}
				flow.AnnotateSend(outmsg, name)
				outmsg.GoSend(msg.ResponseChan)
			case gotocol.GetResponse:
				// return path from a request, send payload back up (not currently used)
			case gotocol.Put:
				// set a key value pair and replicate to other stores
				var key, value string
				fmt.Sscanf(msg.Intention, "%s%s", &key, &value)
				if key != "" && value != "" {
					store[key] = value
					// duplicate the request on to all connected store nodes with the same package name as this one
					for _, n := range microservices.All(names.Package(name)).Names() {
						outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention}
						flow.AnnotateSend(outmsg, name)
						outmsg.GoSend(microservices.Named(n))
					}
				}
			case gotocol.Replicate:
				// Replicate is used between store nodes
				// end point for a request
				var key, value string
				fmt.Sscanf(msg.Intention, "%s%s", &key, &value)
				// log.Printf("store: %v:%v", key, value)
				if key != "" && value != "" {
					store[key] = value
				}
			case gotocol.Goodbye:
				gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(netflixoss)
				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}
				}
			}
		}
	}
}
Example #5
0
// Start priamCassandra, all configuration and state is sent via messages
func Start(listener chan gotocol.Message) {
	// remember the channel to talk to microservices
	microservices := ribbon.MakeRouter()
	// track the hash values owned by each node in the ring
	var ring ByToken
	dependencies := make(map[string]time.Time) // 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, len(archaius.Conf.ZoneNames)*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:
			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: // cross zone = true
				handlers.NameDrop(&dependencies, microservices, msg, name, listener, eureka, true)
			case gotocol.Forget:
				// forget a buddy
				handlers.Forget(&dependencies, microservices, 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))
				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)
					outmsg := gotocol.Message{gotocol.GetResponse, listener, time.Now(), msg.Ctx, store[msg.Intention]}
					flow.AnnotateSend(outmsg, name)
					outmsg.GoSend(msg.ResponseChan)
				} else {
					// forward the message to the right place, but don't change the ResponseChan or span
					outmsg := gotocol.Message{gotocol.GetRequest, msg.ResponseChan, time.Now(), msg.Ctx.AddSpan(), msg.Intention}
					flow.AnnotateSend(outmsg, name)
					outmsg.GoSend(microservices.Named(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
				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 {
						// forward the message to the right place, but don't change the ResponseChan or context parent
						outmsg := gotocol.Message{gotocol.Put, msg.ResponseChan, time.Now(), msg.Ctx.AddSpan(), msg.Intention}
						flow.AnnotateSend(outmsg, name)
						outmsg.GoSend(microservices.Named(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 := range microservices.Names() {
							if names.Region(n) == names.Region(name) && names.Zone(n) == z {
								outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention}
								flow.AnnotateSend(outmsg, name)
								outmsg.GoSend(microservices.Named(n))
								break // only need to send it to one node in each zone
							}
						}
					}
					for _, r := range names.OtherRegions(name, archaius.Conf.RegionNames[0:archaius.Conf.Regions]) {
						for _, n := range microservices.Names() {
							if names.Region(n) == r {
								outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention}
								flow.AnnotateSend(outmsg, name)
								outmsg.GoSend(microservices.Named(n))
								break // only need to send it to one node in each region
							}
						}
					}
				}
			case gotocol.Replicate:
				// Replicate is only used between priamCassandra nodes
				// end point for a request
				var key, value string
				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 {
						// forward the message to the right place, but don't change the ResponseChan
						outmsg := gotocol.Message{gotocol.Replicate, msg.ResponseChan, time.Now(), msg.Ctx, msg.Intention}
						flow.AnnotateSend(outmsg, name)
						outmsg.GoSend(microservices.Named(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
				// find the name matching incoming request channel to see where its coming from
				in := microservices.NameChan(msg.ResponseChan)
				if in != "" && 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 := range microservices.Names() {
							if names.Region(n) == myregion && names.Zone(n) == z {
								outmsg := gotocol.Message{gotocol.Replicate, listener, time.Now(), msg.Ctx.NewParent(), msg.Intention}
								flow.AnnotateSend(outmsg, name)
								outmsg.GoSend(microservices.Named(n))
								break // only need to send it to one node in each zone
							}
						}
					}
					break
				}
			case gotocol.Goodbye:
				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}
				}
			}
		}
	}
}