task 级别的瞬时并发
针对 task 级别的瞬时并发,locust 本身并没有给出解决方案
话不多说帖代码:
from requests.packages.urllib3.exceptions import InsecureRequestWarning
from locust import task, SequentialTaskSet, between, FastHttpUser
from common.prometheus_exporter import *
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# SequentialTaskSet 按顺序执行的 Set
class MomentStart(SequentialTaskSet):
user_item = None
@task
def exams_user_checkExamCondition(self):
with self.client.post("/api1") as res:
assert res.status_code == 200
""" 瞬时并发队列处理 """
# my_queue out_queue 已经在 test_start 阶段被初始化
if not self.user.environment.my_queue.empty() and self.user.environment.out_queue.empty():
# 消费一个my_queue
num = self.user.environment.my_queue.get()
# 阻塞线程
self.user.environment.my_queue.task_done()
if self.user.environment.my_queue.empty():
pass
else:
# my_queue全部消费完之后,才结束阻塞
self.user.environment.my_queue.join()
# 生产一个out_queue
self.user.environment.out_queue.put(num)
@task
def exams_getExamLeaveInfo(self):
with self.client.get("/api2") as res:
assert res.status_code == 200
""" 瞬时并发队列处理 """
if not self.user.environment.out_queue.empty():
# 生产一个my_queue 供下一次循环使用
self.user.environment.my_queue.put(self.user.environment.out_queue.get())
self.user.environment.out_queue.task_done()
class TestUser(FastHttpUser):
wait_time = between(0.5, 1)
tasks = [MomentStart]
分布式时如何处理瞬时并发
在分布式运行的时候,你会发现这个方法失效了,即使你的用户都已经准备完成了,但是并没有进行并发
因为每个 worker 都是 均匀分配运行用户数 的,比如指定了100个用户,启动了2个 worker ,那么每个worker的用户数应该是50,所以需要在 test_start 做初始化,,把并发用户数均匀的分配给每一个 worker
下面这个示例,结合上面的队列处理,就能够解决这个问题,并且可以在WEB UI 中灵活的分配你的并发
# 持续时间
@events.init_command_line_parser.add_listener
def _(parser):
parser.add_argument("--duration-time", include_in_web_ui=True, type=int, env_var="LOCUST_DURATION_TIME", default=300, help="持续时间")
# 用户数量
@events.init_command_line_parser.add_listener
def _(parser):
parser.add_argument("--users-count", include_in_web_ui=True, type=int, env_var="LOCUST_USERS_COUNT", default=100, help="用户数量")
# 每秒孵化用户数
@events.init_command_line_parser.add_listener
def _(parser):
parser.add_argument("--spawn-rate-count", include_in_web_ui=True, type=int, env_var="LOCUST_SPAWN_RATE_COUNT", default=1, help="每秒加载用户数")
# 负载机数量
@events.init_command_line_parser.add_listener
def _(parser: LocustArgumentParser):
parser.add_argument("--worker-count", include_in_web_ui=True, type=int, env_var="LOCUST_WORKER_USER", default=1, help="负载机数量")
# 打印环境日志
@events.test_start.add_listener
def _(environment, **kw):
print(f"Custom argument supplied duration-time: {environment.parsed_options.duration_time}")
print(f"Custom argument supplied users-count: {environment.parsed_options.users_count}")
print(f"Custom argument supplied spawn-rate-count: {environment.parsed_options.spawn_rate_count}")
"""
对队列的处理
!!!结合上面的瞬时并发处理一起使用!!!
"""
@events.test_start.add_listener
def on_locust_init(environment, **kwargs):
# 每个worker的并发用户数 = 总的用户数 / worker总数
concurrence_user = int(environment.parsed_options.users_count / environment.parsed_options.worker_count)
logging.info(f'users_count: {environment.parsed_options.users_count}, concurrence_user: {concurrence_user}, worker_count: {environment.parsed_options.worker_count}')
environment.out_queue = queue.Queue(concurrence_user)
environment.my_queue = queue.Queue(concurrence_user)
# 然后填充队列
for i in range(concurrence_user):
environment.my_queue.put(i)
# 自定义负载时长、负载用户数、用户加载数量
class StagesShape(LoadTestShape):
def stages_details(self):
stages = [
{"duration": self.runner.environment.parsed_options.duration_time,
"users": self.runner.environment.parsed_options.users_count,
"spawn_rate": self.runner.environment.parsed_options.spawn_rate_count},
]
return stages
def tick(self):
run_time = self.get_run_time()
for stage in self.stages_details():
if run_time < stage["duration"]:
tick_data = (stage["users"], stage["spawn_rate"])
return tick_data
return None