// Reload the network from a file func Reload(arch string) string { root := "" g := graphjson.ReadArch(arch) archaius.Conf.Population = 0 // just to make sure // count how many nodes there are for _, element := range g.Graph { if element.Node != "" { archaius.Conf.Population++ } } CreateChannels() CreateEureka() // eureka and edda aren't recorded in the json file to simplify the graph // Start all the services cass := make(map[string]chan gotocol.Message) // for token distribution for _, element := range g.Graph { if element.Node != "" { name := element.Node StartNode(name, "") if names.Package(name) == DenominatorPkg { root = name } if names.Package(name) == "priamCassandra" { cass[name] = noodles[name] // remember the nodes } } } if len(cass) > 0 { // currently doesn't handle multiple priamCassandra per arch priamCassandra.Distribute(cass) // returns a string if it needs logging } // Make all the connections for _, element := range g.Graph { if element.Edge != "" && element.Source != "" && element.Target != "" { Connect(element.Source, element.Target) } } // run for a while if root == "" { log.Fatal("No denominator root microservice specified") } return root }
// Start edda, to listen for logging data from services func Start(name string) { // use a waitgroup so whoever starts edda can tell the logs have been flushed Wg.Add(1) defer Wg.Done() if Logchan == nil { return } var msg gotocol.Message microservices := make(map[string]bool, archaius.Conf.Dunbar) edges := make(map[string]bool, archaius.Conf.Dunbar) var ok bool hist := collect.NewHist(name) log.Println(name + ": starting") if archaius.Conf.GraphmlFile != "" { graphml.Setup(archaius.Conf.GraphmlFile) } if archaius.Conf.GraphjsonFile != "" { graphjson.Setup(archaius.Conf.GraphjsonFile) } if archaius.Conf.Neo4jURL != "" { graphneo4j.Setup(archaius.Conf.Neo4jURL) } for { msg, ok = <-Logchan collect.Measure(hist, time.Since(msg.Sent)) if !ok { break // channel was closed } if archaius.Conf.Msglog { log.Printf("%v(backlog %v): %v\n", name, len(Logchan), msg) } switch msg.Imposition { case gotocol.Inform: edge := names.FilterEdge(msg.Intention) if edges[edge] == false { // only log an edge once edges[edge] = true graphml.WriteEdge(edge) graphjson.WriteEdge(edge, msg.Sent) graphneo4j.WriteEdge(strings.Replace(msg.Intention, "-", "_", -1), msg.Sent) } case gotocol.Put: node := names.FilterNode(msg.Intention) if microservices[node] == false { // only log a node once microservices[node] = true graphml.WriteNode(node + " " + names.Package(msg.Intention)) graphjson.WriteNode(node+" "+names.Package(msg.Intention), msg.Sent) graphneo4j.WriteNode(strings.Replace(msg.Intention, "-", "_", -1)+" "+names.Package(msg.Intention), msg.Sent) } case gotocol.Forget: // forget the edge // problem here in that edges may be reported multiple times from several sources // however after filtering all matching edges are reported as forgotten when the first is // need to maintain the full model and a filtered model with counts edge := names.FilterEdge(msg.Intention) if edges[edge] == true { // only remove an edge once edges[edge] = false graphjson.WriteForget(edge, msg.Sent) } case gotocol.Delete: // remove the node node := names.FilterNode(msg.Intention) if microservices[node] == true { // only remove nodes that exist, and only log it once microservices[node] = false graphjson.WriteDone(node, msg.Sent) } } } log.Println(name + ": closing") graphml.Close() graphjson.Close() graphneo4j.Close() }
// Start a node using the named package, and connect it to any dependencies func StartNode(name string, dependencies ...string) { if names.Package(name) == EurekaPkg { eurekachan[name] = make(chan gotocol.Message, archaius.Conf.Population/len(archaius.Conf.ZoneNames)) // buffer sized to a zone go eureka.Start(eurekachan[name], name) return } else { noodles[name] = make(chan gotocol.Message) } // start the service and tell it it's name switch names.Package(name) { case PiratePkg: go pirate.Start(noodles[name]) case ElbPkg: go elb.Start(noodles[name]) case DenominatorPkg: go denominator.Start(noodles[name]) case ZuulPkg: go zuul.Start(noodles[name]) case KaryonPkg: go karyon.Start(noodles[name]) case MonolithPkg: go monolith.Start(noodles[name]) case StaashPkg: go staash.Start(noodles[name]) case RiakPkg: fallthrough // fake Riak using priamCassandra case PriamCassandraPkg: go priamCassandra.Start(noodles[name]) case CachePkg: fallthrough // fake memcache using store case VolumePkg: fallthrough // fake disk volume using store case StorePkg: go store.Start(noodles[name]) default: log.Fatal("asgard: unknown package: " + names.Package(name)) } noodles[name] <- gotocol.Message{gotocol.Hello, listener, time.Now(), gotocol.NilContext, name} // there is a eureka service registry in each zone, so in-zone services just get to talk to their local registry // elb are cross zone, so need to see all registries in a region // denominator are cross region so need to see all registries globally // priamCassandra depends explicitly on eureka for cross region clusters crossregion := false for _, d := range dependencies { if d == "eureka" { crossregion = true } } for n, ch := range eurekachan { if names.Region(name) == "*" || crossregion { // need to know every eureka in all zones and regions gotocol.Send(noodles[name], gotocol.Message{gotocol.Inform, ch, time.Now(), gotocol.NilContext, n}) } else { if names.Zone(name) == "*" && names.Region(name) == names.Region(n) { // need every eureka in my region gotocol.Send(noodles[name], gotocol.Message{gotocol.Inform, ch, time.Now(), gotocol.NilContext, n}) } else { if names.RegionZone(name) == names.RegionZone(n) { // just the eureka in this specific zone gotocol.Send(noodles[name], gotocol.Message{gotocol.Inform, ch, time.Now(), gotocol.NilContext, n}) } } } } //log.Println(dependencies) // pass on symbolic dependencies without channels that will be looked up in Eureka later for _, dep := range dependencies { if dep != "" && dep != "eureka" { // ignore special case of eureka in dependency list //log.Println(name + " depends on " + dep) gotocol.Send(noodles[name], gotocol.Message{gotocol.NameDrop, nil, time.Now(), gotocol.NilContext, dep}) } } }
// 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} } } } } }