LocustBoomer で負荷試験を行う際、LocustのGUIでターゲットのURLを変更しても、通常はBoomer側には反映されません。 そこでGUIでユーザー数やURLを変更したタイミングで、イベントを受け取れるようにできないかを調べました。

イベントをSubscribeする

こちらの例のように書くことで、各イベント発火時に実行されるcallbackを定義することができます。

https://github.com/myzhan/boomer/blob/master/_examples/events/subscriber.go

package main
 
import (
	"log"
	"time"
 
	"github.com/myzhan/boomer"
)
 
// This is an example about how to subscribe to boomer's internal events.
 
func foo() {
	start := time.Now()
	time.Sleep(100 * time.Millisecond)
	elapsed := time.Since(start)
 
	boomer.RecordSuccess("http", "foo", elapsed.Nanoseconds()/int64(time.Millisecond), int64(10))
}
 
func main() {
	boomer.Events.Subscribe(boomer.EVENT_CONNECTED, func() {
		log.Println("The master sends an ack message")
	})
 
	boomer.Events.Subscribe(boomer.EVENT_SPAWN, func(workers int, spawnRate float64) {
		log.Println("The master asks me to spawn", workers, "goroutines with a spawn rate of", spawnRate, "per second.")
	})
 
	boomer.Events.Subscribe(boomer.EVENT_STOP, func() {
		log.Println("The master asks me to stop.")
	})
 
	boomer.Events.Subscribe(boomer.EVENT_QUIT, func() {
		log.Println("Boomer is quitting now, may be the master asks it to do so, or it receives one of SIGINT and SIGTERM.")
	})
 
	task := &boomer.Task{
		Name:   "foo",
		Weight: 10,
		Fn:     foo,
	}
 
	boomer.Run(task)
}

あらかじめ定義されているイベントは以下のとおりです。

https://github.com/myzhan/boomer/blob/07b7994517513a29ab740e0029b21d0b198fcdff/events.go

	EVENT_CONNECTED = "boomer:connected"
	EVENT_SPAWN     = "boomer:spawn"
	EVENT_STOP      = "boomer:stop"
	EVENT_QUIT      = "boomer:quit"

EVENT_SPAWN であれば、Locustからspawnのメッセージを受け取ったあとの処理のなかで発火されています。 引数に2個指定されているので、callbackも2個の引数を受け取るfuncとなります。

https://github.com/myzhan/boomer/blob/07b7994517513a29ab740e0029b21d0b198fcdff/runner.go#L250

カスタムイベント

自分で定義したイベントを送受信することもできます。

https://github.com/myzhan/boomer/tree/master/_examples/custom_message

boomer.Events.Subscribe("user_defined", func(msg *boomer.CustomMessage) {
	log.Printf("Custom message recv: %v\n", msg)
 
	// Avoid doing lots of type casting between python and go, we stringify data before sending to boomer.
	data, ok := msg.Data.([]byte)
	if !ok {
		log.Println("Failed to cast msg.data to []byte")
	} else {
		log.Printf("data: %s", string(data))
	}
	globalBoomer.SendCustomMessage("acknowledge_users", "Thanks for the message")
})

locustfile.pysend_message にシリアライズしたメッセージを送信することで、Boomer側で受け取ることができます。

for i, worker in enumerate(environment.runner.clients):
    environment.runner.send_message('user_defined', json.dumps(message_body), worker)