如何确保Locust主从模式下的请求计数准确无偏差?
问题详情
采用包含4个Worker的Master-Worker模式进行压测,自行实现了任务执行次数的计数逻辑。但在WebUI点击Stop按钮结束测试后,发现Locust统计的请求数与自实现计数存在偏差,且被测目标服务器实际接收的请求数也与Locust统计数不符。
自定义计数的代码实现
import copy from locust import events, FastHttpUser, task, constant_throughput from locust.runners import MasterRunner from src import utils from src.common import ApiClass from src.common.msg import ResponseMsg from src.common.handle import AccountBase req_count = 0 class CommentTask(FastHttpUser, AccountBase): wait_time = constant_throughput(1) def __init__(self, environment): super().__init__(environment) self.header = copy.deepcopy(ApiClass.Header) @staticmethod def on_count(msg, **_kwargs): global req_count req_count += msg.data['count'] @staticmethod @events.init.add_listener def on_locust_init(environment, **_kwargs): AccountBase.on_locust_init(environment, **_kwargs) CommentTask.host = ApiClass.Host if isinstance(environment.runner, MasterRunner): environment.runner.register_message('on_count', CommentTask.on_count) @staticmethod @events.test_start.add_listener def on_test_start(environment, **_kwargs): AccountBase.on_test_start(environment, **_kwargs) @staticmethod @events.test_stop.add_listener def on_test_stop(environment): if isinstance(environment.runner, MasterRunner): global req_count utils.Log.info(req_count) def on_start(self): self.header['Token'] = self.account.get()['token'] @task def do_comment(self): self.environment.runner.send_message('on_count', {'count': 1}) with self.client.post(url=ApiClass.Path.CommentSave, headers=self.header, json={}, catch_response=True) as response: ResponseMsg.assert_send(self.environment.runner, response)
测试结果对比
- 自定义计数结果:
[INFO]-[2023-08-23 11:16:21]-[Dummy-3:2179754179648]-[comment.py:86] - 1728 - 被测目标服务器实际接收请求数:1725
- WebUI统计数据与上述两者均存在偏差
请问无其他请求干扰时,如何确保Locust请求计数准确?
解决方案
1. 对齐请求实际生命周期统计计数
你当前在任务刚执行就发送计数消息,但此时请求可能还没完成(比如网络中断、请求被取消),导致自定义计数比实际请求数多。改用Locust内置的请求事件统计,这些事件在请求实际完成(成功/失败)后才触发,能和服务器接收数对齐:
# 替换自定义计数逻辑,改用内置事件 req_count = 0 @events.request_success.add_listener def on_request_success(request_type, name, response_time, response_length): global req_count req_count += 1 @events.request_failure.add_listener def on_request_failure(request_type, name, response_time, exception): global req_count req_count += 1 # 删除任务代码里的self.environment.runner.send_message('on_count', {'count': 1})
2. 解决Master节点计数的线程安全问题
全局变量req_count在Master多线程环境下存在竞态条件,多个Worker的计数消息同时到达时可能出现计数丢失。加锁保证计数操作的原子性:
import threading req_count = 0 count_lock = threading.Lock() @staticmethod def on_count(msg, **_kwargs): global req_count with count_lock: req_count += msg.data['count']
3. 停止测试时等待Worker消息同步
点击Stop后,Master可能还没接收完所有Worker的计数消息就输出结果,导致计数不全。在test_stop事件中等待所有Worker完成消息同步:
@staticmethod @events.test_stop.add_listener def on_test_stop(environment): if isinstance(environment.runner, MasterRunner): # 等待所有Worker完成状态同步 environment.runner.worker_state_machines.wait_all() global req_count utils.Log.info(req_count)
4. 直接使用Locust内置统计模块
Locust本身提供了完善的统计API,无需自行实现计数逻辑,直接通过内置统计获取准确请求数:
@staticmethod @events.test_stop.add_listener def on_test_stop(environment): if isinstance(environment.runner, MasterRunner): total_requests = environment.runner.stats.total.num_requests utils.Log.info(f"Locust内置统计总请求数:{total_requests}")
内容的提问来源于stack exchange,提问作者Strawberry
相关产品推荐
相关产品推荐

