性能工具之 Locust 工具关联与参数化

网友投稿 614 2022-05-30

前言

不同的压力工具在参数化的实现逻辑上也会不同,但是参数化必须依赖业务逻辑,而不是工具中能做到什么功能。所以在参数化之前,我们必须分析真实业务逻辑中如何使用数据,再在工具中选择相对应的组合参数的方式去实现。

参数化

locust 工具中有怎么使用参数化完成工作,在开展工作开始前,先了解 Python 中的一个 Queue 类,queue它是一个队列数据结构是先进先出的数据结构,具体原理大家自己查询即可。

Queue种类:

FIFO: Queue.Queue(maxsize=0) FIFO即First in First Out,先进先出。Queue提供了一个基本的FIFO容器,使用方法很简单,maxsize是个整数,指明了队列中能存放的数据个数的上限。一旦达到上限,插入会导致阻塞,直到队列中的数据被消费掉。如果maxsize小于或者等于0,队列大小没有限制。 LIFO Queue.LifoQueue(maxsize=0) LIFO即Last in First Out,后进先出。与栈的类似,使用也很简单,maxsize用法同上。 priority class Queue.PriorityQueue(maxsize=0) 构造一个优先队列。maxsize用法同上。

基本方法:

Queue.Queue(maxsize=0) FIFO, 如果maxsize小于1就表示队列长度无限

Queue.LifoQueue(maxsize=0) LIFO, 如果maxsize小于1就表示队列长度无限

Queue.qsize() 返回队列的大小

Queue.empty() 如果队列为空,返回True,反之False

Queue.full() 如果队列满了,返回True,反之False

Queue.get([block[, timeout]]) 读队列,timeout等待时间

性能工具之 Locust 工具关联与参数化

Queue.put(item, [block[, timeout]]) 写队列,timeout等待时间

Queue.queue.clear() 清空队列

队列 queue 多应用在多线程应用中,多线程访问共享变量。对于多线程而言,访问共享变量时,队列queue是线程安全的。

有以上简单基础知识后,直接上 Locust 脚本怎么编写参数化,直接仿照即可写出参数脚本,下面是简单一个参数化代码。

import csv import os, requests import queue from locust import TaskSet, task, HttpUser from requests.packages.urllib3.exceptions import InsecureRequestWarning # 禁用安全请求警告 requests.packages.urllib3.disable_warnings(InsecureRequestWarning) def fnReadData(): f = open("uuid.text", "r") #读取参数文件 data = [] #声明空列表 data = csv.reader(f) #通过 csv读取文件内容 s = queue.Queue() #实例化一个queue对象 for each in data: #循环读取open里面的数据 for key in each: try: s.put_nowait(key) #put到队列中 except queue.Full: print("Queue overflow") f.close() return s class MyBlogs(TaskSet): # 访问我的博客首页 @task(1) def get_blog(self): # 定义请求头 header = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36"} data = self.user.queueData.get() req = self.client.get("/357712148/%s" % data, headers=header, verify=False) if req.status_code == 200: print("success") else: print("fails") class httpGet(HttpUser): tasks = [MyBlogs] min_wait = 3000 # 单位为毫秒 max_wait = 6000 # 单位为毫秒 queueData = fnReadData() # 队列实例化 if __name__ == "__main__": #通过好 os.system("locust -f lcome.py --host=https://blog.com --headless -u 1 -r 1 -t 1s")

参数化文件:

运行结果:

[2021-04-25 13:39:51,536] liwen.local/INFO/locust.main: Run time limit set to 1 seconds [2021-04-25 13:39:51,536] liwen.local/INFO/locust.main: Starting Locust 1.4.4 [2021-04-25 13:39:51,536] liwen.local/INFO/locust.runners: Spawning 1 users at the rate 1 users/s (0 users already running)... [2021-04-25 13:39:51,536] liwen.local/INFO/locust.runners: All users spawned: httpGet: 1 (1 total running) [2021-04-25 13:39:51,537] liwen.local/INFO/root: Terminal was not a tty. Keyboard input disabled Name # reqs # fails | Avg Min Max Median | req/s failures/s -------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------- Aggregated 0 0(0.00%) | 0 0 0 0 | 0.00 0.00 success success success [2021-04-25 13:39:52,536] liwen.local/INFO/locust.main: Time limit reached. Stopping Locust. [2021-04-25 13:39:52,537] liwen.local/INFO/locust.runners: Stopping 1 users [2021-04-25 13:39:52,537] liwen.local/INFO/locust.runners: 1 Users have been stopped, 0 still running [2021-04-25 13:39:52,537] liwen.local/INFO/locust.main: Running teardowns... [2021-04-25 13:39:52,537] liwen.local/INFO/locust.main: Shutting down (exit code 0), bye. [2021-04-25 13:39:52,537] liwen.local/INFO/locust.main: Cleaning up runner... Name # reqs # fails | Avg Min Max Median | req/s failures/s -------------------------------------------------------------------------------------------------------------------------------------------- GET /357712148/2525651 1 0(0.00%) | 238 238 238 238 | 1.30 0.00 GET /357712148/2532241 1 0(0.00%) | 210 210 210 210 | 1.30 0.00 GET /357712148/2562906 1 0(0.00%) | 320 320 320 320 | 1.30 0.00 -------------------------------------------------------------------------------------------------------------------------------------------- Aggregated 3 0(0.00%) | 256 210 320 240 | 3.89 0.00 Response time percentiles (approximated) Type Name 50% 66% 75% 80% 90% 95% 98% 99% 99.9% 99.99% 100% # reqs --------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------| GET /357712148/2525651 240 240 240 240 240 240 240 240 240 240 240 1 GET /357712148/2532241 210 210 210 210 210 210 210 210 210 210 210 1 GET /357712148/2562906 320 320 320 320 320 320 320 320 320 320 320 1 --------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------| None Aggregated 240 240 320 320 320 320 320 320 320 320 320 3

关联

相对于关联都是获取响应结果来做入参,关联理论请参考关联和断言:一动一静,核心都是在取数据 这里面理论已经讲解很清楚,现在看看 locust 怎么关联数据。

打开项目工程 关键内容如下:

/** * 需要关联的数据 * * @param id * @return */ @GetMapping("/associated/{id}") @ResponseBody public R associated(@PathVariable Integer id) { HashMap map = new HashMap<>(); if (StringUtils.isEmpty(id)) { return R.error(); } else if (id == 1) { HttpHeaders headers = new HttpHeaders(); //根据自己的需要动态添加你想要的content type headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); map.put("des", "关联数据"); map.put("rew", 6666); map.put("header", headers); return R.ok().put("data", map); } else { return R.error().put("data", map); } } /** * 需要关联的数据 * * @param id * @return */ @GetMapping("/associated/data/{id}") @ResponseBody public R associatedData(@PathVariable Integer id) { HashMap map = new HashMap<>(); if (StringUtils.isEmpty(id)) { return R.error(); } else if (id == 6666) { map.put("des", "关联数据成功"); map.put("rew", 8888); return R.ok().put("data", map); } else { return R.error().put("data", map); } }

locust 脚本参考:

def get_param(self): '''获取参数''' response = self.client.get("/associated/1") print("Response json:", type(response.json())) # 判断类型 print("Response json:", response.json()) res = response.json() # 转换字典 return res['data']['rew'] # 获取参数 @task(1) def request_param(self): '''关联参数请求''' id = self.get_param() response = self.client.get("/associated/data/%s" % id) print("Response json:", type(response.json())) # 判断类型 print("Response json:", response.json())

结果:

[2021-04-25 22:52:25,837] liwen.local/INFO/locust.main: Run time limit set to 1 seconds [2021-04-25 22:52:25,837] liwen.local/INFO/locust.main: Starting Locust 1.4.4 [2021-04-25 22:52:25,837] liwen.local/INFO/locust.runners: Spawning 1 users at the rate 1 users/s (0 users already running)... [2021-04-25 22:52:25,838] liwen.local/INFO/locust.runners: All users spawned: webTestDunShan: 1 (1 total running) [2021-04-25 22:52:25,838] liwen.local/INFO/root: Terminal was not a tty. Keyboard input disabled Name # reqs # fails | Avg Min Max Median | req/s failures/s -------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------- Aggregated 0 0(0.00%) | 0 0 0 0 | 0.00 0.00 Response json: Response json: {'msg': 'success', 'code': 0, 'data': {'des': '关联数据', 'rew': 6666, 'header': {'Content-Type': ['application/json']}}} Response json: Response json: {'msg': 'success', 'code': 0, 'data': {'des': '关联数据成功', 'rew': 8888}} ..... 略 .... Response json: {'msg': 'success', 'code': 0, 'data': {'des': '关联数据成功', 'rew': 8888}} Response json: Response json: {'msg': 'success', 'code': 0, 'data': {'des': '关联数据', 'rew': 6666, 'header': {'Content-Type': ['application/json']}}} [2021-04-25 22:52:26,705] liwen.local/INFO/locust.main: Time limit reached. Stopping Locust. [2021-04-25 22:52:26,705] liwen.local/INFO/locust.runners: Stopping 1 users [2021-04-25 22:52:26,705] liwen.local/INFO/locust.runners: 1 Users have been stopped, 0 still running [2021-04-25 22:52:26,705] liwen.local/INFO/locust.main: Running teardowns... [2021-04-25 22:52:26,705] liwen.local/INFO/locust.main: Shutting down (exit code 0), bye. [2021-04-25 22:52:26,705] liwen.local/INFO/locust.main: Cleaning up runner... Name # reqs # fails | Avg Min Max Median | req/s failures/s -------------------------------------------------------------------------------------------------------------------------------------------- GET /associated/1 406 0(0.00%) | 0 0 5 1 | 468.35 0.00 GET /associated/data/6666 405 0(0.00%) | 1 0 7 1 | 467.20 0.00 -------------------------------------------------------------------------------------------------------------------------------------------- Aggregated 811 0(0.00%) | 0 0 7 1 | 935.55 0.00 Response time percentiles (approximated) Type Name 50% 66% 75% 80% 90% 95% 98% 99% 99.9% 99.99% 100% # reqs --------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------| GET /associated/1 1 1 1 1 1 1 1 1 5 5 5 406 GET /associated/data/6666 1 1 1 1 1 1 1 2 7 7 7 405 --------|------------------------------------------------------------|---------|------|------|------|------|------|------|------|------|------|------|------| None Aggregated

总结

Locust 的参数化与关联需要自己写相应代码才能完成,但对性能工程师来说学习 Python 代码是不可绕过去的事情。上面是简单的参数化与关联代码希望对大家有帮助,

Python 任务调度 压力测试

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:scala快速入门系列【Actor并发编程】
下一篇:打通C/4HANA和S/4HANA的一个原型开发:智能服务创新案例
相关文章