Locust入門記事で負荷試験はとりあえず実行できたけど、もっとよく知りたい人向けの記事です。
本記事は
http://docs.locust.io/en/latest/
に載っている情報をTipsとしてまとめたものです。
サンプルコードも同サイトのものです。
※英語力十分の方は上記サイトを見た方がいいです
Locust クラス
min_waitとmax_wait
それぞれのタスクの実行間隔はmin_waitとmax_waitの間のランダム値になります。
実際のユーザーはひっきりなしにAPIを実行しません。
ブログサイトなら本文を見る時間が、ゲームアプリでも内容よってはAPIとAPIの実行間隔が空きます。
ある程度ユーザーの行動が予測できる場合はmin_waitとmax_waitを適切な値に設定するとより実際の運用時に近い負荷になるでしょう。
デフォルトは min_wait=500, max_wait=1500 になっています。
(単位はミリ秒)
from locust import Locust, TaskSet, task
class MyTaskSet(TaskSet):
@task
def my_task(self):
print "executing my_task"
class MyLocust(Locust):
task_set = MyTaskSet
min_wait = 5000
max_wait = 15000
min_waitとmax_waitを設定するときはオーバーライドします。
ちなみに
self.wait()
はmin_waitとmax_waitを使用してランダム時間ウェイトします。
タスク関数に分けられないけど待ち時間なしで連続で実行されたくない
ユーザー登録のテスト等に使えるのではないでしょうか。
タスクの重み付け
ウェブサイトやウェブアプリは全てのページが均等に閲覧、訪問されることはまずありません。
トップページが一番多く、利用規約等はあまり頻繁に閲覧されることはないでしょう。
このためにタスクには実行の重み付けをすることができます。
class WebUserLocust(Locust):
weight = 3
....
class MobileUserLocust(Locust):
weight = 1
....
上記のような構成の場合、WebUserLocustはMobileUserLocustは3倍の頻度で実行されます。
TaskSet クラス
Locustクラスがイナゴの群れだとしたらTaskSetクラスはイナゴの脳とのことです。
Locustクラスが全体の振る舞いを決めてTaskSetクラスでそれぞれがどう動くかを決める感じでしょうか。
(和訳力が低いのでうまく伝えられません・・・)
タスク関数の明示的な宣言
TaskSetクラスの中でメンバ関数に対してtaskデコレータを付けることにより明示的にタスク関数であることを宣言できます。
from locust import Locust, TaskSet, task
class MyTaskSet(TaskSet):
@task
def my_task(self):
print "Locust instance (%r) executing my_task" % (self.locust)
class MyLocust(Locust):
task_set = MyTaskSet
逆にtaskデコレータを付けないでクラス内の共通処理関数を実装する等の使い方になりそうです。
またこのtaskデコレータは引数により前述の重み付けが設定できます。
from locust import Locust, TaskSet, task
class MyTaskSet(TaskSet):
min_wait = 5000
max_wait = 15000
@task(3)
def task1(self):
pass
@task(6)
def task2(self):
pass
class MyLocust(Locust):
task_set = MyTaskSet
Locustクラスでの重み付けとの違いはモジュール単位か関数単位かです。
(ウェブサイトで言うとページ単位かページ内のアクション単位か)
タスクセットのネスト化
ウェブサイトやウェブアプリはページが階層化されていることが多いです。
それに合わせてタスクセットもネスト化することによりより実際のユーザーの行動に合わせた
シナリオを書くことができます。
また、タスクセットをモジュールのように分割しておくことにより様々な組み合わせの
シナリオを最小限のコードで実装することができます。
class ForumPage(TaskSet):
@task(20)
def read_thread(self):
pass
@task(1)
def new_thread(self):
pass
@task(5)
def stop(self):
self.interrupt()
class UserBehaviour(TaskSet):
tasks = {ForumPage:10}
@task
def index(self):
pass
アプリの規模が大きくなればなるほどAPIも増えていくのでメンテナンス性を考えて
できるだけタスクセットは分割しておいた方がいいでしょう。
interrupt( )
上コードで stop() 関数で self.interruput() を実行しています。
interruput()は現在のタスクセットを終了し、上位タスクセットに再度タスク振り分けを
させる関数です。これを呼ばない場合、Locustインスタンスは延々ForumPage内のタスクを
実行し続けます。実際のユーザーは色々なページを移動するはずなので適宜 interrupt関数を
実行することが重要です。
と、かなり重要な内容でありながら
http://docs.locust.io/en/latest/
を読んでいない場合 interrupt() を明示的に実行する発想にはなかなかならないでしょう・・・
デコレータはクラスにも適用できるため次のような書き方もできます。
class MyTaskSet(TaskSet):
@task
class SubTaskSet(TaskSet):
@task
def my_task(self):
pass
ただやみくもにネスト化しても複雑になるだけなのでうまい書き方を見つけたいですね。
on_start
on_start()はタスクセット開始時に呼ばれます。
APIに必要なHTTPヘッダー等はここで設定するといいでしょう。
以下 HTTPLocust等は入門記事で紹介されているかと思いますので省略します。
この他役立つ情報が多く書かれていますので
http://docs.locust.io/en/latest/
の一読をお勧めします。(英語のみですが)
またLocustのソースコードははgithubにありますので見てみるのも面白いです。
https://github.com/locustio