将 Locust 作为库运行时触发事件挂钩
Posted
技术标签:
【中文标题】将 Locust 作为库运行时触发事件挂钩【英文标题】:Firing Event Hooks when running Locust as a library 【发布时间】:2021-06-23 21:45:27 【问题描述】:我正在尝试使用 Locust 库对 API 端点执行负载测试。在这里,我将 Locust 作为库运行,而不是使用 locust
命令。我正在尝试执行全局设置和全局拆卸,以便最初创建一个供所有用户使用的全局状态,然后在拆卸时清除(例如,下载 S3 文件一次,然后在最后将其删除)。
有内置的事件挂钩来添加此功能,例如init
和quitting
,可以在使用locust
命令运行 locustfile 时使用。但是,当它作为库运行时,我无法触发这些事件。根据 Locust 的源代码,我可以检查这些事件是否在 locust main.py
文件中被触发,但在作为库运行时不会被调用。
-
作为库运行时如何添加此类事件?我尝试过以下两种方法。添加事件侦听器并手动调用
event.fire()
是一种正确的方法,还是直接为其创建和调用自定义方法而不是使用事件是更好的方法?
一般情况下,应该使用init
和quitting
事件来初始设置全局状态,然后在结束时清除,还是也可以使用test_start
和test_stop
事件来代替它?
源代码供参考:
方法 - 1(使用事件挂钩)
import gevent
from locust import HttpUser, task, between
from locust.env import Environment
from locust.stats import stats_printer, stats_history
from locust.log import setup_logging
from locust import events
setup_logging("INFO", None)
def on_init(environment, **kwargs):
print("Perform global setup to create a global state")
def on_quit(environment, **kwargs):
print('Perform global teardown to clear the global state')
events.quitting.add_listener(on_quit)
events.init.add_listener(on_init)
class User(HttpUser):
wait_time = between(1, 3)
host = "https://docs.locust.io"
@tas
def my_task(self):
self.client.get("/")
@task
def task_404(self):
self.client.get("/non-existing-path")
# setup Environment and Runner
env = Environment(user_classes=[User], events=events)
runner = env.create_local_runner()
### Fire init event and environment and local runner have been instantiated
env.events.init.fire(environment=env, runner=runner) # Is it correct approach?
# start a WebUI instance
env.create_web_ui("127.0.0.1", 8089)
# start a greenlet that periodically outputs the current stats
gevent.spawn(stats_printer(env.stats))
# start a greenlet that save current stats to history
gevent.spawn(stats_history, env.runner)
# start the test
env.runner.start(1, spawn_rate=10)
# in 5 seconds stop the runner
gevent.spawn_later(5, lambda: env.runner.quit())
# wait for the greenlets
env.runner.greenlet.join()
### Fire quitting event when locust process is exiting
env.events.quitting.fire(environment=env, reverse=True) # Is it correct approach?
# stop the web server for good measures
env.web_ui.stop()
方法 - 2(创建自定义方法并直接调用它们)
import gevent
from locust import HttpUser, task, between
from locust.env import Environment
from locust.stats import stats_printer, stats_history
from locust.log import setup_logging
from locust import events
setup_logging("INFO", None)
class User(HttpUser):
wait_time = between(1, 3)
host = "https://docs.locust.io"
@classmethod
def perform_global_setup(cls):
print("Perform global setup to create a global state")
@classmethod
def perform_global_teardown(cls):
print('Perform global teardown to clear the global state')
@task
def my_task(self):
self.client.get("/")
@task
def task_404(self):
self.client.get("/non-existing-path")
# setup Environment and Runner
env = Environment(user_classes=[User])
runner = env.create_local_runner()
### Perform global setup
for cls in env.user_classes:
cls.perform_global_setup() # Is it correct approach?
# start a WebUI instance
env.create_web_ui("127.0.0.1", 8089)
# start a greenlet that periodically outputs the current stats
gevent.spawn(stats_printer(env.stats))
# start a greenlet that save current stats to history
gevent.spawn(stats_history, env.runner)
# start the test
env.runner.start(1, spawn_rate=10)
# in 5 seconds stop the runner
gevent.spawn_later(5, lambda: env.runner.quit())
# wait for the greenlets
env.runner.greenlet.join()
### Perform global teardown
for cls in env.user_classes:
cls.perform_global_teardown() # Is it correct approach?
# stop the web server for good measures
env.web_ui.stop()
【问题讨论】:
【参考方案1】:这两种方法都很好。如果您认为将来可能希望以正常(而不是作为库)方式运行,则使用事件挂钩更有意义,但如果这不太可能发生,请选择您觉得最自然的方法。
在 gui 模式下进行多次运行时(其中 test_start/stop 可能发生多次),init/quitting 与 test_start/stop 的区别仅在有意义的方面。使用适合您在事件处理程序中执行的操作,没有其他指南。
【讨论】:
感谢您的建议。我将使用 Approach-2(使用自定义方法),因为它将来更有可能用作库。以上是关于将 Locust 作为库运行时触发事件挂钩的主要内容,如果未能解决你的问题,请参考以下文章
通过 Model.save() 更新时触发 Mongoose 预保存挂钩