// create eureka service registries in each zone func CreateEureka() { // setup name service and cross zone replication links znames := archaius.Conf.ZoneNames Create("eureka", EurekaPkg, archaius.Conf.Regions, len(archaius.Conf.ZoneNames)) for n, ch := range eurekachan { var n1, n2 string switch names.Zone(n) { case znames[0]: n1 = znames[1] n2 = znames[2] case znames[1]: n1 = znames[0] n2 = znames[2] case znames[2]: n1 = znames[0] n2 = znames[1] } for nn, cch := range eurekachan { if names.Region(nn) == names.Region(n) && (names.Zone(nn) == n1 || names.Zone(nn) == n2) { //log.Println("Eureka cross connect from: " + n + " to " + nn) gotocol.Send(ch, gotocol.Message{gotocol.NameDrop, cch, time.Now(), gotocol.NilContext, nn}) } } } }
// send a message directly to a name via asgard, only used during setup func SendToName(name string, msg gotocol.Message) { if noodles[name] != nil { gotocol.Send(noodles[name], msg) } else { log.Fatal("Asgard can't send to " + name) } }
// Tell a source node how to connect to a target node directly by name, only used when Eureka can't be used func Connect(source, target string) { if noodles[source] != nil && noodles[target] != nil { gotocol.Send(noodles[source], gotocol.Message{gotocol.NameDrop, noodles[target], time.Now(), gotocol.NilContext, target}) //log.Println("Link " + source + " > " + target) } else { log.Fatal("Asgard can't link " + source + " > " + target) } }
// 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 } } } } }
// distribute tokens to one zone of a cassandra cluster, this doesn't yet allow for clusters to grow or replace nodes func Distribute(cass map[string]chan gotocol.Message) string { size := len(cass) // each node owns a share of the full range hashrange := uint32(0xFFFFFFFF) / uint32(size) // make a config string of the form cass1:0,cass4:1000,cass2:2000 i := 0 s := "" for n, _ := range cass { s += fmt.Sprintf("%s:%v,", n, hashrange*uint32(i)) i++ } s = strings.TrimSuffix(s, ",") // send the config to each node, repurposing the Chat message type as a kind of Gossip setup for _, c := range cass { gotocol.Send(c, gotocol.Message{gotocol.Chat, nil, time.Now(), gotocol.NilContext, s}) } return s // for logging and test }
// connect to eureka services in every region func ConnectEveryEureka(name string) { for n, ch := range eurekachan { gotocol.Send(noodles[name], gotocol.Message{gotocol.Inform, ch, time.Now(), gotocol.NilContext, n}) } }
// 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}) } } }