// Start fsm and create new pirates func Start() { listener = make(chan gotocol.Message) // listener for fsm if archaius.Conf.Population < 2 { log.Fatal("fsm: can't create less than 2 pirates") } // create map of channels and a name index to select randoml nodes from noodles = make(map[string]chan gotocol.Message, archaius.Conf.Population) pnames = make([]string, archaius.Conf.Population) // indexable name list log.Println("fsm: population", archaius.Conf.Population, "pirates") for i := 1; i <= archaius.Conf.Population; i++ { name := names.Make(archaius.Conf.Arch, "atlantic", "bermuda", "blackbeard", "pirate", i) noodles[name] = make(chan gotocol.Message) go pirate.Start(noodles[name]) } i := 0 msgcount := 1 start := time.Now() for name, noodle := range noodles { pnames[i] = name i++ // tell the pirate it's name and how to talk back to it's fsm // this must be the first message the pirate sees noodle <- gotocol.Message{gotocol.Hello, listener, time.Now(), gotocol.NilContext, name} if edda.Logchan != nil { // tell the pirate to report itself and new edges to the logger noodle <- gotocol.Message{gotocol.Inform, edda.Logchan, time.Now(), gotocol.NilContext, ""} msgcount = 2 } } log.Println("fsm: Talk amongst yourselves for", archaius.Conf.RunDuration) rand.Seed(int64(len(noodles))) for _, name := range pnames { // for each pirate tell them about two other random pirates noodle := noodles[name] // lookup the channel // pick a first random pirate to tell this one about talkto := pnames[rand.Intn(len(pnames))] noodle <- gotocol.Message{gotocol.NameDrop, noodles[talkto], time.Now(), gotocol.NewTrace(), talkto} // pick a second random pirate to tell this one about talkto = pnames[rand.Intn(len(pnames))] noodle <- gotocol.Message{gotocol.NameDrop, noodles[talkto], time.Now(), gotocol.NewTrace(), talkto} // anonymously send this pirate a random amount of GoldCoin up to 100 gold := fmt.Sprintf("%d", rand.Intn(100)) noodle <- gotocol.Message{gotocol.GoldCoin, nil, time.Now(), gotocol.NewTrace(), gold} // tell this pirate to start chatting with friends every 0.1 to 10 secs delay := fmt.Sprintf("%dms", 100+rand.Intn(9900)) noodle <- gotocol.Message{gotocol.Chat, nil, time.Now(), gotocol.NewTrace(), delay} } msgcount += 4 d := time.Since(start) log.Println("fsm: Delivered", msgcount*len(pnames), "messages in", d) shutdown() }
// 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 } } } }
// Reload the network from a file func Reload(arch string) { listener = make(chan gotocol.Message) // listener for fsm log.Println("fsm reloading from " + arch + ".json") g := graphjson.ReadArch(arch) pop := 0 // count how many nodes there are for _, element := range g.Graph { if element.Node != "" { pop++ } } archaius.Conf.Population = pop // create the map of channels noodles = make(map[string]chan gotocol.Message, archaius.Conf.Population) // Start all the services for _, element := range g.Graph { if element.Node != "" && element.Service != "" { name := element.Node noodles[name] = make(chan gotocol.Message) // start the service and tell it it's name switch element.Service { case "pirate": go pirate.Start(noodles[name]) noodles[name] <- gotocol.Message{gotocol.Hello, listener, time.Now(), gotocol.NilContext, name} if edda.Logchan != nil { // tell the pirate to report itself and new edges to the logger noodles[name] <- gotocol.Message{gotocol.Inform, edda.Logchan, time.Now(), gotocol.NilContext, ""} } default: log.Println("fsm: unknown service: " + element.Service) } } } // Make all the connections for _, element := range g.Graph { if element.Edge != "" && element.Source != "" && element.Target != "" { noodles[element.Source] <- gotocol.Message{gotocol.NameDrop, noodles[element.Target], time.Now(), gotocol.NewTrace(), element.Target} log.Println("Link " + element.Source + " > " + element.Target) } } // send money and start the pirates chatting for _, noodle := range noodles { // same as below for now, but will save and read back from file later // anonymously send this pirate a random amount of GoldCoin up to 100 gold := fmt.Sprintf("%d", rand.Intn(100)) noodle <- gotocol.Message{gotocol.GoldCoin, nil, time.Now(), gotocol.NewTrace(), gold} // tell this pirate to start chatting with friends every 0.1 to 10 secs delay := fmt.Sprintf("%dms", 100+rand.Intn(9900)) noodle <- gotocol.Message{gotocol.Chat, nil, time.Now(), gotocol.NilContext, delay} } shutdown() }
func TestFlow(t *testing.T) { archaius.Conf.Collect = true archaius.Conf.Arch = "test" s1 := gotocol.NewTrace() m1 := gotocol.Message{gotocol.GetRequest, nil, time.Now(), s1, "customer1"} AnnotateSend(m1, "requestor") // pretend there is a subscriber service that got a message from a requestor AnnotateReceive(m1, "subscriber", time.Now()) s2 := m1.Ctx.NewParent() // pretend that the names and addresses services were both sent messages by subscriber m2 := gotocol.Message{gotocol.GetRequest, nil, time.Now(), s2, m1.Intention} AnnotateSend(m2, "subscriber") s3 := s2.AddSpan() m3 := gotocol.Message{gotocol.GetRequest, nil, time.Now(), s3, m1.Intention} AnnotateSend(m3, "subscriber") // pretend that names got the message and returned to subscriber AnnotateReceive(m2, "names", time.Now()) m4 := gotocol.Message{gotocol.GetResponse, nil, time.Now(), m2.Ctx, "name:Fred Flintstone"} AnnotateSend(m4, "names") // pretend that addresses got the message and returned to subscriber AnnotateReceive(m3, "addresses", time.Now()) m5 := gotocol.Message{gotocol.GetResponse, nil, time.Now(), m3.Ctx, "address:Bedrock"} AnnotateSend(m5, "addresses") // pretend that subscriber got both messages and joined them together AnnotateReceive(m4, "subscriber", time.Now()) AnnotateReceive(m5, "subscriber", time.Now()) m6 := gotocol.Message{gotocol.GetResponse, nil, time.Now(), s1, "name:Fred Flintstone, address:Bedrock"} AnnotateSend(m6, "subscriber") // pretend that requestor got the message AnnotateReceive(m6, "requestor", time.Now()) fmt.Println("All flows") for _, f := range flowmap { for _, a := range f { fmt.Println(*a) } } fmt.Println("\nWrite all remaining flows in order to file") Shutdown() }
// 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) } } } }
// Start the pirate, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := 10 // starting point for how many buddies to remember // remember the channel to talk to named buddies buddies := make(map[string]chan gotocol.Message, dunbar) // remember who sent GoldCoin and how much, to buy favors benefactors := make(map[string]int, dunbar) var booty int // current GoldCoin balance var fsm chan gotocol.Message // remember how to talk back to creator var name string // remember my name var logger chan gotocol.Message // if set, send updates var chatrate time.Duration hist := collect.NewHist("") 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 fsm = msg.ResponseChan // remember how to talk to my namer name = msg.Intention // message body is my name hist = collect.NewHist(name) } case gotocol.Inform: // remember where to send updates logger = handlers.Inform(msg, name, listener) case gotocol.NameDrop: // don't remember too many buddies and don't talk to myself buddy := msg.Intention // message body is buddy name if len(buddies) < dunbar && buddy != name { // remember how to talk to this buddy buddies[buddy] = msg.ResponseChan // message channel is buddy's listener if logger != nil { // if it's setup, tell the logger I have a new buddy to talk to logger <- gotocol.Message{gotocol.Inform, listener, time.Now(), gotocol.NilContext, name + " " + buddy} } } 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) // assume we got paid before we started chatting rand.Seed(int64(booty)) } case gotocol.GoldCoin: var coin int _, e := fmt.Sscanf(msg.Intention, "%d", &coin) if e == nil && coin > 0 { booty += coin for name, ch := range buddies { if ch == msg.ResponseChan { benefactors[name] += coin } } } case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away with %v gold coins, chatting every %v\n", name, booty, chatrate) } gotocol.Message{gotocol.Goodbye, nil, time.Now(), gotocol.NilContext, name}.GoSend(fsm) return } case <-chatTicker.C: if rand.Intn(100) < 50 { // 50% of the time // use Namedrop to tell the last buddy about the first var firstBuddyName string var firstBuddyChan, lastBuddyChan chan gotocol.Message if len(buddies) >= 2 { for name, ch := range buddies { if firstBuddyName == "" { firstBuddyName = name firstBuddyChan = ch } else { lastBuddyChan = ch } gotocol.Message{gotocol.NameDrop, firstBuddyChan, time.Now(), gotocol.NewTrace(), firstBuddyName}.GoSend(lastBuddyChan) } } } else { // send a buddy some money if booty > 0 { donation := rand.Intn(booty) luckyNumber := rand.Intn(len(buddies)) if donation > 0 { for _, ch := range buddies { if luckyNumber == 0 { gotocol.Message{gotocol.GoldCoin, listener, time.Now(), gotocol.NewTrace(), fmt.Sprintf("%d", donation)}.GoSend(ch) booty -= donation break } else { luckyNumber-- } } } } } } } }