// 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() }
func TestFlow(t *testing.T) { r1 := gotocol.NewTrace() r2 := gotocol.NewTrace() r3 := gotocol.NewTrace() archaius.Conf.Arch = "test" Begin(r1, "one") Begin(r2, "two") Begin(r3, "three") Update(r1.NewSpan(), "une") Update(r2.NewSpan(), "deux") Update(r3.NewSpan(), "trois") fmt.Println(flowmap) End(r1) Shutdown() }
func TestFlow(t *testing.T) { archaius.Conf.Collect = true r1 := gotocol.NewTrace() r2 := gotocol.NewTrace() r3 := gotocol.NewTrace() archaius.Conf.Arch = "test" Update(r1, "one") Update(r2, "two") Update(r3, "three") Update(r1.NewSpan(), "une") Update(r2.NewSpan(), "deux") Update(r3.NewSpan(), "trois") fmt.Println(flowmap) End(r1) fmt.Println("Walk") Walk(flowmap) 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() }
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"} Annotate(m1, "ss", "requestor", m1.Sent) // BUG can't Annotate(m1...) twice as it overwrites the record // pretend there is a subscriber service that got a message from a requestor a1 := Annotate(m1, "sr", "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, AnnotateSend(a1, s2), s2, m1.Intention} s3 := s2.AddSpan() m3 := gotocol.Message{gotocol.GetRequest, nil, AnnotateSend(a1, s3), s3, m1.Intention} // pretend that names returned to subscriber a2 := Annotate(m2, "sr", "names", time.Now()) s4 := m2.Ctx.NewParent() m4 := gotocol.Message{gotocol.GetResponse, nil, AnnotateSend(a2, s4), s4, "name:Fred Flintstone"} // pretend that addresses returned to subscriber a3 := Annotate(m3, "sr", "addresses", time.Now()) s5 := m3.Ctx.NewParent() m5 := gotocol.Message{gotocol.GetResponse, nil, AnnotateSend(a3, s5), s5, "address:Bedrock"} // pretend that subscriber got both messages and joined them together Annotate(m4, "sr", "subscriber", time.Now()) // first return doesn't do anything else a5 := Annotate(m5, "sr", "subscriber", time.Now()) s6 := m5.Ctx.NewParent() m6 := gotocol.Message{gotocol.GetResponse, nil, AnnotateSend(a5, s6), s6, "name:Fred Flintstone, address:Bedrock"} // pretend that requestor got the message Annotate(m6, "sr", "requestor", time.Now()) fmt.Println("All flows") fmt.Println(flowmap) fmt.Println("\nWalk all remaining flows") PrintWalk(flowmap) fmt.Println("\nWalk all remaining flows in order to file") Walk(flowmap, 0) fmt.Println("\nEnd trace 1") End(m1.Ctx) Shutdown() }
// 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 = gotocol.InformHandler(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-- } } } } } } } }
// Start the denominator, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := 6 // starting point for how many nodes to remember // remember the channel to talk to microservices microservices := make(map[string]chan gotocol.Message, dunbar) microindex := make([]chan gotocol.Message, dunbar) dependencies := make(map[string]time.Time, dunbar) // dependent services and time last updated var parent chan gotocol.Message // remember how to talk back to creator var name string // remember my name hist := collect.NewHist("") // don't know name yet eureka := make(map[string]chan gotocol.Message, 3*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() 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 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] = gotocol.InformHandler(msg, name, listener) case gotocol.NameDrop: gotocol.NameDropHandler(&dependencies, µservices, msg, name, listener, eureka) case gotocol.Forget: // forget a buddy gotocol.ForgetHandler(&dependencies, µservices, 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 flow.End(msg.Ctx) case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away, was chatting every %v\n", name, chatrate) } 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: if len(microservices) > 0 { // build index if needed if len(microindex) != len(microservices) { i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) // start a request to a random member of this denominator ctx := gotocol.NewTrace() flow.Begin(ctx, name) if rand.Intn(2) == 0 { gotocol.Message{gotocol.GetRequest, listener, time.Now(), ctx, "why?"}.GoSend(microindex[m]) } else { gotocol.Message{gotocol.Put, listener, time.Now(), ctx, "remember me"}.GoSend(microindex[m]) } } } } }
// Start monolith, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := 30 // starting point for how many nodes to remember // remember the channel to talk to microservices microservices := make(map[string]chan gotocol.Message, dunbar) microindex := make([]chan gotocol.Message, dunbar) dependencies := make(map[string]time.Time, dunbar) // dependent services and time last updated var netflixoss, requestor 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, 1) // service registry var chatrate time.Duration hist := collect.NewHist("") ep, _ := time.ParseDuration(archaius.Conf.EurekaPoll) eurekaTicker := time.NewTicker(ep) 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 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] = gotocol.InformHandler(msg, name, listener) case gotocol.NameDrop: // monolith talks cross zones, only difference from karyon gotocol.NameDropHandler(&dependencies, µservices, msg, name, listener, eureka, true) case gotocol.Forget: // forget a buddy gotocol.ForgetHandler(&dependencies, µservices, 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.GetRequest: // route the request on to microservices requestor = msg.ResponseChan // Intention body indicates which service to route to or which key to get // need to lookup service by type rather than randomly call one day if len(microservices) > 0 { if len(microindex) != len(microservices) { // rebuild index i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) // start a request to a random service gotocol.Message{gotocol.GetRequest, listener, time.Now(), msg.Ctx.NewSpan(), msg.Intention}.GoSend(microindex[m]) } case gotocol.GetResponse: // return path from a request, send payload back up if requestor != nil { gotocol.Message{gotocol.GetResponse, listener, time.Now(), msg.Ctx.NewSpan(), msg.Intention}.GoSend(requestor) } case gotocol.Put: // route the request on to a random dependency if len(microservices) > 0 { if len(microindex) != len(microservices) { // rebuild index i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) // pass on request to a random service gotocol.Message{gotocol.Put, listener, time.Now(), msg.Ctx.NewSpan(), msg.Intention}.GoSend(microindex[m]) } case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away\n", name) } 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} } } case <-chatTicker.C: if len(microservices) > 0 { if len(microservices) != len(microindex) { // rebuild index i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) // start a request to a random member of this elb gotocol.Message{gotocol.GetRequest, listener, time.Now(), gotocol.NewTrace(), name}.GoSend(microindex[m]) } //default: } } }
// Start the denominator, all configuration and state is sent via messages func Start(listener chan gotocol.Message) { dunbar := 6 // starting point for how many nodes to remember // remember the channel to talk to microservices microservices := make(map[string]chan gotocol.Message, dunbar) microindex := make([]chan gotocol.Message, dunbar) dependencies := make(map[string]time.Time, dunbar) // dependent services and time last updated var parent chan gotocol.Message // remember how to talk back to creator var name string // remember my name hist := collect.NewHist("") // don't know name yet eureka := make(map[string]chan gotocol.Message, 3*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, 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, µservices, msg, name, listener, eureka) case gotocol.Forget: // forget a buddy handlers.Forget(&dependencies, µservices, 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 flow.End(msg.Ctx) case gotocol.Goodbye: if archaius.Conf.Msglog { log.Printf("%v: Going away, was chatting every %v\n", name, chatrate) } 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: if len(microservices) > 0 { // build index if needed if len(microindex) != len(microservices) { i := 0 for _, ch := range microservices { microindex[i] = ch i++ } } m := rand.Intn(len(microservices)) // start a request to a random member of this denominator 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(microindex[m]) } } } }