Example #1
0
File: fsm.go Project: jgcsco/spigo
// 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()
}
Example #2
0
// 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
			}
		}
	}
}
Example #3
0
File: fsm.go Project: jgcsco/spigo
// 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()
}
Example #4
0
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()
}
Example #5
0
// 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)
			}
		}
	}
}
Example #6
0
// 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--
							}
						}
					}
				}
			}
		}
	}
}