// 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) } }
// Delete a single node from the given service func Delete(noodles *map[string]chan gotocol.Message, service string) { if service != "" { for node, ch := range *noodles { if names.Service(node) == service { gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NewTrace(), "chaosmonkey"}.GoSend(ch) log.Println("chaosmonkey delete: " + node) return } } } }
// 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 eureka discovery service and set name directly func Start(listener chan gotocol.Message, name string) { // use a waitgroup so whoever starts eureka can tell it's ready and when stopping that the logs have been flushed Wg.Add(1) defer Wg.Done() var msg gotocol.Message var ok bool hist := collect.NewHist(name) microservices := make(map[string]chan gotocol.Message, archaius.Conf.Dunbar) eurekaservices := make(map[string]chan gotocol.Message, 2) metadata := make(map[string]meta, archaius.Conf.Dunbar) lastrequest := make(map[callback]time.Time) // remember time of last request for a service from this requestor log.Println(name + ": starting") for { msg, ok = <-listener collect.Measure(hist, time.Since(msg.Sent)) if !ok { break // channel was closed } //commented out because name service traffic is too much noise in the log //if archaius.Conf.Msglog { // log.Printf("%v(backlog %v): %v\n", name, len(listener), msg) //} switch msg.Imposition { // used to wire up connections to other eureka nodes only case gotocol.NameDrop: if msg.Intention != name { // don't talk to myself eurekaservices[msg.Intention] = msg.ResponseChan } // for new nodes record the data, replicate and maybe pass on to be logged case gotocol.Put: if microservices[msg.Intention] == nil { // ignore duplicate requests microservices[msg.Intention] = msg.ResponseChan metadata[msg.Intention] = meta{true, msg.Sent} // replicate request, everyone ends up with the same timestamp for state change of this service for _, c := range eurekaservices { gotocol.Message{gotocol.Replicate, msg.ResponseChan, msg.Sent, gotocol.NilContext, msg.Intention}.GoSend(c) } if edda.Logchan != nil { edda.Logchan <- msg } } case gotocol.Replicate: if microservices[msg.Intention] == nil { // ignore multiple requests microservices[msg.Intention] = msg.ResponseChan metadata[msg.Intention] = meta{true, msg.Sent} } case gotocol.Inform: // don't store edges in discovery but do log them if edda.Logchan != nil { edda.Logchan <- msg } case gotocol.GetRequest: if msg.Intention == "" { log.Fatal(name + ": empty GetRequest") } if microservices[msg.Intention] != nil { // matched a unique full name gotocol.Message{gotocol.NameDrop, microservices[msg.Intention], time.Now(), gotocol.NilContext, msg.Intention}.GoSend(msg.ResponseChan) break } for n, ch := range microservices { // respond with all the online names that match the service component if names.Service(n) == msg.Intention { // if there was an update for the looked up service since last check // log.Printf("%v: matching %v with %v, last: %v metadata: %v\n", name, n, msg.Intention, lastrequest[callback{n, msg.ResponseChan}], metadata[n].registered) if metadata[n].registered.After(lastrequest[callback{n, msg.ResponseChan}]) { if metadata[n].online { gotocol.Message{gotocol.NameDrop, ch, time.Now(), gotocol.NilContext, n}.GoSend(msg.ResponseChan) } else { //log.Printf("%v:Forget %v\n", name, n) gotocol.Message{gotocol.Forget, ch, time.Now(), gotocol.NilContext, n}.GoSend(msg.ResponseChan) } } // remember for next time lastrequest[callback{n, msg.ResponseChan}] = msg.Sent } } case gotocol.Delete: // remove a node if microservices[msg.Intention] != nil { // matched a unique full name metadata[msg.Intention] = meta{false, time.Now()} // replicate request for _, c := range eurekaservices { gotocol.Message{gotocol.Replicate, nil, time.Now(), gotocol.NilContext, msg.Intention}.GoSend(c) } if edda.Logchan != nil { edda.Logchan <- msg } } case gotocol.Goodbye: gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(msg.ResponseChan) log.Println(name + ": closing") return } } }
// record messages in neo4j as well as zipkin func WriteFlow(source, target, call string, tnano int64, trace gotocol.TraceContextType) { if Enabled == false { return } if epoch == 0 { epoch = tnano } Write(fmt.Sprintf("MATCH (from:%v {name: %q}), (to:%v {name: %q})\nCREATE (from)-[:%v {arch:%q, timenano:%v, trace:%v}]->(to)", names.Service(source), names.Instance(source), names.Service(target), names.Instance(target), call, archaius.Conf.Arch+ss, tnano-epoch, trace)) }
// WriteEdge writes the edge to a file given a space separated from and to node name func WriteEdge(fromTo string, t time.Time) { if Enabled == false { return } var source, target string fmt.Sscanf(fromTo, "%s%s", &source, &target) // two space delimited names tstamp := t.Format(time.RFC3339Nano) Write(fmt.Sprintf("MATCH (from:%v {name: %q}), (to:%v {name: %q})\nCREATE (from)-[:CONN {arch:%q, timestamp:%q}]->(to)", names.Service(source), names.Instance(source), names.Service(target), names.Instance(target), archaius.Conf.Arch+ss, tstamp)) }
// WriteNode writes the node to a file given a space separated name and service type func WriteNode(nameService string, t time.Time) { if Enabled == false { return } var node, pack string fmt.Sscanf(nameService, "%s%s", &node, &pack) // space delimited tstamp := t.Format(time.RFC3339Nano) // node id should be unique and package indicates service type nodestmt, err := db.Prepare(fmt.Sprintf("CREATE (:%v:%v:%v {name:{0}, node:{1}, timestamp:{2}, ip:{3}, region:{4}, zone:{5}})", archaius.Conf.Arch+ss, pack, names.Service(node))) if err != nil { log.Fatal(err) } _, err = nodestmt.Exec(names.Instance(node), node, tstamp, dhcp.Lookup(node), names.Region(node), names.Zone(node)) if err != nil { log.Fatal(err) } nodestmt.Close() }