はじめに
12月がきた!!!!
負荷テストツール locustを実際に業務で使って大変いい感じだったので紹介します。
locustとは
locustと調べて一番上に出てくるのはバッタかと思います。 locust jsと調べるとHPにたどり着きます。
無料で使えてUIがいい感じの いい感じの負荷テストツールです。
pythonで書きます。 python業務で使ったことなかったですが、すごい楽にテスト記載できました
どんな結果が得られるのか?
CLIでもいい感じに結果出してくれます
[2023-11-30 22:01:21,743] enoMacBook-Pro.local/INFO/locust.main: Shutting down (exit code 1)
Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
GET /hello 24468 0(0.00%) | 0 0 3 1 | 736.73 0.00
GET /world 24467 24467(100.00%) | 0 0 14 1 | 736.70 736.70
--------|----------------------------------------------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 48935 24467(50.00%) | 0 0 14 1 | 1473.43 736.70
Response time percentiles (approximated)
Type Name 50% 66% 75% 80% 90% 95% 98% 99% 99.9% 99.99% 100% # reqs
--------|--------------------------------------------------------------------------------|--------|------|------|------|------|------|------|------|------|------|------|------
GET /hello 1 1 1 1 1 1 1 1 2 2 4 24468
GET /world 1 1 1 1 1 1 1 1 2 3 14 24467
--------|--------------------------------------------------------------------------------|--------|------|------|------|------|------|------|------|------|------|------|------
Aggregated 1 1 1 1 1 1 1 1 2 2 14 48935
Error report
# occurrences Error
------------------|---------------------------------------------------------------------------------------------------------------------------------------------
24467 GET /world: HTTPError('404 Client Error: Not Found for url: /world')
------------------|---------------------------------------------------------------------------------------------------------------------------------------------
結果のダウンロードもできます。 CSVとHTML形式でのダウンロードが可能です。
はじめかた
インストール手順に従ってインストールしてください。
locustfile.pyを作成します。
from locust import HttpUser, task
class HelloWorldUser(HttpUser):
@task
def hello_world(self):
self.client.get("/hello")
self.client.get("/world")
/hello と /worldに負荷をかける手順です。
locustfile.pyがあるディレクトリで次のコマンドを実行してください。
locust
こんな感じの起動ログが出たら起動に成功してます。
% locust
[2023-11-30 21:33:27,891] enoMacBook-Pro.local/INFO/locust.main: Starting web interface at http://0.0.0.0:8089 (accepting connections from all network interfaces)
[2023-11-30 21:33:27,905] enoMacBook-Pro.local/INFO/locust.main: Starting Locust 2.19.1
http://0.0.0.0:8089 にアクセスすると、次のようなページが表示されます。
Number of usersは同時ユーザーのピーク数を表します。例えば50にしたら、ある時点では50ユーザが作成され、アクセスが行われます。
Spawn rateはユーザーを生成するレート (1 秒あたりのユーザー数)です。
Hostは負荷テストを行う先を指定します。
他にもたくさんのオプションがあります。
参考: https://docs.locust.io/en/stable/configuration.html#all-available-configuration-options
実行してみよう
テストのためにローカルでサーバを起動します。
http://localhost:8080/hello を作ります。
お好きな方法で起動してもらって大丈夫です。
※次の手順をする時、もしnodeをインストールしていなければインストールからお願いします。
npm init -y
npm install --save-dev connect
server.jsを作成します。
const connect = require('connect');
const app = connect();
app.use('/hello', function(req, res) {
res.end('Hello!');
});
app.listen(8080, function() {
console.log('Hello World is running on port 8080...');
});
起動します。
node server.js
負荷をかけてみよう
Hostに起動したローカルサーバのホスト( http://localhost:8080 )を指定します。
Start swarmingをクリックします。
リアルタイムに数値が更新されていく様子が確認できましたでしょうか。
今回はhelloだけ用意したので、helloは全て成功してworldは全て失敗していますね。
実際の負荷試験では、右上のFAILURESの値を見て、数値が上がってきたら失敗の原因を確認するために、FaiuresやExceptionsを確認したり、サーバーのログを見たり、一度負荷試験を止めて負荷を下げた試験をやったりします。
いろんなことができます
豊富なevents handler
from locust import HttpUser, events, task
@events.init.add_listener
def request_handler(request_type, name, response_time, response_length, response, context, exception, start_time, url, **kwargs):
print('init')
initのタイミングで処理を挟めたりします。
token取得処理をinitでして global変数につめたり...
おまけ: こんな感じで使ってました
.
├── docker-compose.yml
├── locust.conf
├── locustfile.py
├── sequential_task
│ ├── hoge.py
│ └── fuga.py
└── test_data
├── aaa.py
└── bbb.xlsx
def nanka_data() -> dict:
return {
"x": "なんかデータ", "y":"なんかでーた2"
}
def nanka_morota_data(ukewatasi: str) -> dict:
return {
"x": "なんかもろたデータ", "y": ukewatasi
}
import test_data.aaa as t
from locust import task, SequentialTaskSet
class Hoge(SequentialTaskSet):
# 1回目に実行されて欲しいtask
@task
def first(self):
response = self.client.post("/aaa", json=t.nanka_data())
self.ukewatasi = response.json()["XXX"]["YYY"]
# 2回目に実行されて欲しいtask
@task
def second(self):
self.client.post(
"/bbb",
json=t.nanka_morota_data(self.ukewatasi),
)
SequentialTaskSetは宣言されているtaskの順序でtaskを実行してくれます。
ukewatasi のように selfに対して値を設定してtaskを跨いだ値の受け渡しが可能です。
1回だけlogin処理をしてtokenを受け渡すようなこともできます。
import requests
from sequential_task.hoge import Hoge
from locust import HttpUser, between, events
@events.init.add_listener
def locust_init(environment, **kwargs):
global token
token = requests.post(environment.host + "/login", {"id": "nankaId", "password": "nankaPassword"}).text
class Hoge(HttpUser):
def on_start(self):
self.client.headers = {"Authorization": "Bearer " + token }
wait_time = between(60, 180)
tasks = [Hoge]
classは実行したい手順(シナリオ)単位で用意したら便利でした。
host = http://localhost:8080
class-picker = true
locust.confにclass-pickerを指定することで1class(1シナリオ)ずつ負荷をかけることも可能になります。
普段はdocker-compose upで起動してます
version: '3.5'
services:
locust:
image: locustio/locust
ports:
- "8089:8089"
volumes:
- ./:/mnt/locust
working_dir: /mnt/locust
command: --config=/mnt/locust/locust.conf -f /mnt/locust/locustfile.py
extra_hosts:
- "localhost:host-gateway"