将 Locust 作为库运行时触发事件挂钩

Posted

技术标签:

【中文标题】将 Locust 作为库运行时触发事件挂钩【英文标题】:Firing Event Hooks when running Locust as a library 【发布时间】:2021-06-23 21:45:27 【问题描述】:

我正在尝试使用 Locust 库对 API 端点执行负载测试。在这里,我将 Locust 作为库运行,而不是使用 locust 命令。我正在尝试执行全局设置和全局拆卸,以便最初创建一个供所有用户使用的全局状态,然后在拆卸时清除(例如,下载 S3 文件一次,然后在最后将其删除)。

有内置的事件挂钩来添加此功能,例如initquitting,可以在使用locust 命令运行 locustfile 时使用。但是,当它作为库运行时,我无法触发这些事件。根据 Locust 的源代码,我可以检查这些事件是否在 locust main.py 文件中被触发,但在作为库运行时不会被调用。

    作为库运行时如何添加此类事件?我尝试过以下两种方法。添加事件侦听器并手动调用event.fire() 是一种正确的方法,还是直接为其创建和调用自定义方法而不是使用事件是更好的方法? 一般情况下,应该使用initquitting 事件来初始设置全局状态,然后在结束时清除,还是也可以使用test_starttest_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 预保存挂钩

主题动作挂钩一次性功能

OnTokenValidated 事件在应用程序部署为 Azure Web App 时未触发,但在本地运行时触发

为啥我在运行 Locust 时收到 403 错误?

Locust 分布式运行

Locust 分布式运行