更新
最終的なlocustfile.py
にself.client.get("/basket.html")
とself.client.post("/orders")
を入れ忘れていたので追記しました.
やること
マイクロサービスのデモアプリであるSock Shopについて,locustを用いて負荷テストをしてみます.
作業
Sock Shopでは開発者が用意したload testがありますが,こちらをreadme通りに試してみたところ上手くいきませんでした.
Default Locust file: locustfile.py
Will run locustfile.py against http://hikida-v6:30001. Spawning 8089 clients and 10 total requests.
Traceback (most recent call last):
File "/home/hikida/.local/bin/locust", line 8, in <module>
sys.exit(main())
File "/home/hikida/.local/lib/python3.8/site-packages/locust/main.py", line 68, in main
docstring, _user_classes, shape_class = load_locustfile(_locustfile)
File "/home/hikida/.local/lib/python3.8/site-packages/locust/util/load_locustfile.py", line 58, in load_locustfile
imported = source.load_module()
File "<frozen importlib._bootstrap_external>", line 522, in _check_name_wrapper
File "<frozen importlib._bootstrap_external>", line 1027, in load_module
File "<frozen importlib._bootstrap_external>", line 852, in load_module
File "<frozen importlib._bootstrap>", line 265, in _load_module_shim
File "<frozen importlib._bootstrap>", line 702, in _load
File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 848, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "/home/hikida/load-test/locustfile.py", line 27, in <module>
class Web(HttpLocust):
File "/home/hikida/.local/lib/python3.8/site-packages/locust/util/deprecation.py", line 27, in __new__
raise ImportError(deprecation_message)
ImportError: The HttpLocust class has been renamed to HttpUser in version 1.0. For more info see: https://docs.locust.io/en/latest/changelog.html#changelog-1-0
done
恐らくlocustのversion違いが原因かなと
先程のgitにはlocustfile.py
があります.
import base64
from locust import HttpLocust, TaskSet, task
from random import randint, choice
class WebTasks(TaskSet):
@task
def load(self):
base64string = base64.encodestring('%s:%s' % ('user', 'password')).replace('\n', '')
catalogue = self.client.get("/catalogue").json()
category_item = choice(catalogue)
item_id = category_item["id"]
self.client.get("/")
self.client.get("/login", headers={"Authorization":"Basic %s" % base64string})
self.client.get("/category.html")
self.client.get("/detail.html?id={}".format(item_id))
self.client.delete("/cart")
self.client.post("/cart", json={"id": item_id, "quantity": 1})
self.client.get("/basket.html")
self.client.post("/orders")
class Web(HttpLocust):
task_set = WebTasks
min_wait = 0
max_wait = 0
こちらをこのままlocust -f locustfile.py
で実行したところ
ImportError: The HttpLocust class has been renamed to HttpUser in version 1.0. For more info see:
https://docs.locust.io/en/latest/changelog.html#changelog-1-0
やはりversionの問題のようです.
また,base64
の書き方もよくなさそうなので書き直します.
locust -f locustfile.py
で実行してみます.
[2022-12-22 06:12:27,320] hikida-db/INFO/locust.main: Shutting down (exit code 1)
Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------|------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
GET / 151 0(0.00%) | 43 1 49 44 | 4.98 0.00
GET /basket.html 150 0(0.00%) | 44 41 49 44 | 4.95 0.00
DELETE /cart 151 0(0.00%) | 8 5 88 7 | 4.98 0.00
POST /cart 151 0(0.00%) | 19 11 135 14 | 4.98 0.00
GET /catalogue 151 0(0.00%) | 7 4 26 6 | 4.98 0.00
GET /category.html 151 0(0.00%) | 3 1 45 2 | 4.98 0.00
GET /detail.html?id=03fef6ac-1896-4ce8-bd69-b798f85c6e0b 22 0(0.00%) | 3 1 43 2 | 0.73 0.00
GET /detail.html?id=3395a43e-2d88-40de-b95f-e00e1502085b 14 0(0.00%) | 4 1 47 2 | 0.46 0.00
GET /detail.html?id=510a0d7e-8e83-4193-b483-e27e09ddc34d 21 0(0.00%) | 8 1 49 2 | 0.69 0.00
GET /detail.html?id=808a2de1-1aaa-4c25-a9b9-6612e8f29a38 9 0(0.00%) | 6 1 47 2 | 0.30 0.00
GET /detail.html?id=819e1fbf-8b7e-4f6d-811f-693534916a8b 19 0(0.00%) | 6 1 50 2 | 0.63 0.00
GET /detail.html?id=837ab141-399e-4c1f-9abc-bace40296bac 16 0(0.00%) | 4 1 43 2 | 0.53 0.00
GET /detail.html?id=a0a4f044-b040-410d-8ead-4de0446aec7e 19 0(0.00%) | 3 1 44 2 | 0.63 0.00
GET /detail.html?id=d3588630-ad8e-49df-bbd7-3167f7efb246 17 0(0.00%) | 9 1 49 2 | 0.56 0.00
GET /detail.html?id=zzz4f044-b040-410d-8ead-4de0446aec7e 14 0(0.00%) | 15 1 51 2 | 0.46 0.00
GET /login 151 0(0.00%) | 15 10 96 13 | 4.98 0.00
POST /orders 150 22(14.67%) | 50 24 188 47 | 4.95 0.73
--------|------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 1357 22(1.62%) | 22 1 188 13 | 44.74 0.73
エラーが出ずに動作しましたが,/orders
のfailが目立ちます.なぜだ...
考えていたところ,fail数22と同じ数のリクエストが送られている商品があることに気が付きます.
GET /detail.html?id=03fef6ac-1896-4ce8-bd69-b798f85c6e0b 22
どうやらSock Shopでは100$以上の注文をするとこのようにエラーが発生するようです.
この画像のHoly
という商品がカートに入れられてしまうとTotalが100$を超えてしまいます.
locustfile.py
ではカタログにある商品をランダムで選ぶように設定されているのでHoly
が選ばれないようにする必要がありそうです.
Sock Shopを編集して値段や上限の変更が出来れば良いのですが難易度が高そうなので,locustfile.py
を編集する方向でいきます.
最終的なlocustfile.py
がこちら
import base64
from locust import HttpUser, task
from random import randint, choice
class WebTasks(HttpUser):
@task
def load(self):
base64string = base64.b64encode(('%s:%s' % ('user', 'password')).encode()).decode()
catalogue = self.client.get("/catalogue").json()
## delete Holy
count = 0
for dic in catalogue:
if dic['name'] == 'Holy':
catalogue.pop(count)
count += 1
category_item = choice(catalogue)
item_id = category_item["id"]
self.client.get("/")
self.client.get("/login", headers={"Authorization":"Basic %s" % base64string})
self.client.get("/category.html")
self.client.get("/detail.html?id={}".format(item_id))
self.client.delete("/cart")
self.client.post("/cart", json={"id": item_id, "quantity": 1})
self.client.get("/basket.html")
self.client.post("/orders")
catalogue = self.client.get("/catalogue").json()
の中身は辞書型となっていてHoly
は先頭に記述されていました.順番が変わることを考慮して辞書内からHoly
を探して削除しています.
無事正常に動作しました!
[2022-12-22 07:06:47,049] hikida-db/INFO/locust.main: Shutting down (exit code 0)
Type Name # reqs # fails | Avg Min Max Med | req/s failures/s
--------|------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
~~~
POST /orders 147 0(0.00%) | 42 28 125 39 | 5.40 0.00
--------|------------------------------------|-------|-------------|-------|-------|-------|-------|--------|-----------
Aggregated 1328 0(0.00%) | 20 1 125 12 | 48.75 0.00
おわりに
私は大学でマイクロサービスについて研究をしています.今回はSock Shopを用いて実験している中で負荷テストが必要となったので,locustを用いてやってみました.研究する上で今回のように,既に用意されているファイルの書き方が古く上手くいかないケースが何度かあり大変でした.同じく悩んでいる方にこの記事が役立てば幸いです.
何かあれば気軽にコメントお願いします!