基于locust全链路压测系统
2021年中旬就计划着搭建一套压测系统,大约9月份已经搭建完成,使用至今还是比较稳定了,分享一下搭建思路及过程:
为什么选择Locust呢,因为Locust可以仅需要执行命令就可以完成压测任务,并且集群压测也很简单,只需压测机安装locust并把压测脚本推送到服务器即可。
Locust QQ群:

画了一个大致的思路图:

我们说的全链路其实有几层意思:
1.多接口多场景,而非单接口或单url
2.按照用户访问场景及频率,用户访问的路径是有先后的,访问的接口频率也是不一样的。怎么理解这个呢,很简单,比如获取列表的接口(get_list)和获取内容的接口(get_content),用户访问任何页面有可能都会访问
get_list,但用户可能都不会点击详情,所以调用get_list的频率会更多。
怎么真实的获取到用户访问的链路场景呢?
1.通过用户访问的日志,分析用户的行为,然后编写压测场景用例
2.模拟用户场景,导出用户记录
A.浏览器直接导出记录生成.har文件
B.app通过抓包工具获取用户记录导出生成.har文件
当然有的人说har文件解析生成接口后,后续压测能一直有效么,比如token等校验通不过,解决这个问题很简单,和研发商量一下,请求参数里加每个值或对特定设备或标识放开就行,后续一路畅通无阻。
压测脚本来源有了,第二步就是解析har文件,模块库里有解析har的,但发现不满足自己使用,自己写吧,项目结构仅供参考:

解析Har文件:
1 # -*- coding = utf-8 -*-
2 # ------------------------------
3 # @time: 2021/3/22 14:53
4 # @Author: drew_gg
5 # @File: disassemble_har.py
6 # @Software: cover_app_platform
7 # ------------------------------
8
9 import json
10 from app.locust.anasiysis_har import judgment_exist as jud
11 from app.locust.anasiysis_har import deal_headers as dh
12 from app.locust.anasiysis_har import deal_request_data as dr
13 from app.config.har_to_api import api_filter as af
14
15
16 key_words = af.key_words
17
18
19 def disassemble_har(har_file, api_only=0):
20 """
21 提取分解har文件
22 :param har_file: .har文件
23 :param api_only: 1:去重,其他:不去重
24 :return:
25 """
26
27 req_l = []
28 rdl = []
29 rdl_set = []
30 host = ''
31 count = 1
32 # url过滤非接口请求
33 with open(har_file, "r", encoding='utf-8') as f:
34 f = json.loads(f.read())
35 for i in f['log']['entries']:
36 if jud.judgment_exist(i['request']['url'], key_words) is False:
37 req_l.append(i)
38 for index, i in enumerate(req_l):
39 rd = {}
40 # 解析host
41 host = i['request']['url'].split('//')[0] + '//' + i['request']['url'].split('//')[1].split('/')[0]
42 # 解析子url
43 # son_url = i['request']['url'].split(host)[1].split('&')[0]
44 son_url = i['request']['url'].split(host)[1]
45 deal_url = son_url.split('?')[0]
46 if deal_url == '/':
47 if len(son_url.split('?'))> 1:
48 deal_url = son_url.split('?')[1]
49 else:
50 deal_url = '/'
51 deal_url = deal_url.replace('/', '_').replace('-', '_').replace('.', '_').strip('_').lstrip('_')
52 if api_only == 1:
53 method_name = 'api_' + deal_url.lower()
54 else:
55 method_name = 'api_' + deal_url.lower() + '_' + str(index)
56 # 解析处理header
57 headers = dh.deal_headers(i['request']['headers'])
58 method = i['request']['method']
59 # 解析处理请求参数
60 if method.upper() == "POST":
61 request_data = dr.deal_request_data(method, i['request']['postData'])
62 if method.upper() == "GET":
63 request_data = '\'' + i['request']['url'].split(son_url)[1] + '\''
64 host = '"' + host + '"'
65 son_url = '"' + son_url + '"'
66 rd['host'] = host
67 rd['url'] = son_url
68 rd['headers'] = headers
69 rd['method'] = method
70 rd['method_name'] = method_name
71 rd['request_data'] = request_data
72 if api_only == 1:
73 # 去重并计数判断
74 if index == 0:
75 rd['count'] = count
76 rdl_set.append(rd)
77 else:
78 for x in rdl_set:
79 if son_url == x['url']:
80 x['count'] += 1
81 count = x['count']
82 else:
83 if count == 1:
84 rd['count'] = count
85 rdl_set.append(rd)
86 count = 1
87 else:
88 rd['count'] = count
89 rdl.append(rd)
90 if api_only != 1:
91 rdl_set = rdl
92 return rdl_set, host
93
94
95 if __name__ == '__main__':
96 har_path = r'D:\thecover_project\cover_app_platform\app\file_upload\首页普通\20210803-113719\syptxq.har'
97 disassemble_har(har_path)
解析har文件,处理header、获取接口必要参数,然后对请求做分析,如果要去重,则统计相同请求的数量,压测时生成压测权重,如果不去重,后续生成压测脚本时则需要对处理方法名称。
解析好har文件后,需要生成调试脚本和压测脚本:
我处理方式实直接生成py文件,事先创建好模板,如:

生成调试脚本比较简单,只需要一个模板就行,生成locust压测脚本则稍微负责点,我是分拆成多个模板,然后整合到一个模板。
生成的脚本都规范放在目录里:

生成脚本目录结构:

生成压测脚本示例:
1 # -*- coding = utf-8 -*-
2 # ------------------------------
3 # @time: 2021-04-19 13:43:10.380837
4 # @Author: drew_gg
5 # @File: liao_bao.py
6 # @Software: api_locust
7 # ------------------------------
8
9
10 from locust import SequentialTaskSet, task, constant, tag, TaskSet
11 from locust.contrib.fasthttp import FastHttpUser
12
13
14 class LiaoBao20210419(TaskSet):
15
16 @task(1)
17 @tag('api_getlist')
18 def api_getlist(self):
19 headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'tenantId': '7'}
20 # 请求参数组装 ## r_url:固定参数
21 r_url = "/getList?vno=6.4.0"
22 requests_data = {'account': 'E2247B94-51E2-4952-BC06-24752911C060', 'client': 'iOS', 'data': '{"operation_type":0,"news_id":0,xxxxxxxxxxxxxxxxxxx'}
23 # 发起请求
24 with self.client.post(r_url, data=requests_data, catch_response=True, name=r_url) as r:
25 if r.content == b"":
26 r.failure("No data")
27 if r.status_code != 200:
28 em = "request error --" + str(r.status_code)
29 r.failure(em)
30
31 @task(4)
32 @tag('api_getsysnotice')
33 def api_getsysnotice(self):
34 headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'tenantId': '7'}
35 # 请求参数组装 ## r_url:固定参数
36 r_url = "/getSysnotice?vno=6.4.0"
37 requests_data = {'account': 'E251179A-6309-4326-9827-73C892131605', 'client': 'iOS', 'data': '{"page_size":15,"page":1}', xxxxxxxxxxxxxxxxxxxxxxxx}
38 # 发起请求
39 with self.client.post(r_url, data=requests_data, catch_response=True, name=r_url) as r:
40 if r.content == b"":
41 r.failure("No data")
42 if r.status_code != 200:
43 em = "request error --" + str(r.status_code)
44 r.failure(em)
45
46 @task(4)
47 @tag('api_user_preparecancelaccount')
48 def api_user_preparecancelaccount(self):
49 headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'tenantId': '7'}
50 # 请求参数组装 ## r_url:固定参数
51 r_url = "/user/prepareCancelAccount?vno=6.4.0"
52 requests_data = {'account': '2FF3D47C-995B-4D7E-93CD-58B4F1E94B74', 'client': 'iOS', 'data': '{}', xxxxxxxxxxxxxxxxxxxxxxx}
53 # 发起请求
54 with self.client.post(r_url, data=requests_data, catch_response=True, name=r_url) as r:
55 if r.content == b"":
56 r.failure("No data")
57 if r.status_code != 200:
58 em = "request error --" + str(r.status_code)
59 r.failure(em)
60
61
62 class liao_bao_locust(FastHttpUser):
63 host = "https://xxxxxx.xxxxx.com"
64 wait_time = constant(0)
65 tasks = {LiaoBao20210419: 1}
生成好脚本后,需要生成执行命令:
1 # -*- coding = utf-8 -*-
2 # ------------------------------
3 # @time: 2021/3/3 11:08
4 # @Author: drew_gg
5 # @File: locust_create_cmd.py
6 # @Software: cover_app_platform
7 # ------------------------------
8
9
10 def create_master_cmd(locust_pra):
11 """
12 生成master命令
13 :param locust_pra:
14 :return:
15 """
16 # locust master 命令样式:
17 """
18 locust -f /work/locust/api_locust/locust_view/fm_api/locust_api/locust_fm_640.py
19 --master
20 --master-bind-port 9800
21 --headless
22 -u 600
23 -r 200
24 --expect-worker 16
25 -t 10m
26 -s 10
27 --csv /work/locust/locust_report/fm/locust_get_dynamic.py0223145309
28 --html /work/locust/api_locust/resource/html/new_html/locust_get_operation_parm.html
29 """
30 run_port = '9800'
31 master_cmd = "locust -f %s --master --master-bind-port %s --headless " % (locust_pra['to_file'], run_port)
32 master_pra = "-u %s -r %s --expect-worker %s -t %ss -s 10 --csv %s --html %s > %s" % \
33 (locust_pra['user'], locust_pra['rate'], locust_pra['thread'], locust_pra['time'], locust_pra['csv'],
34 locust_pra['html'], locust_pra['master_log'])
35 master_cmd = master_cmd + master_pra
36 return master_cmd
37
38
39 def create_slave_cmd(locust_pra):
40 """
41 生成slave命令
42 :return:
43 """
44 run_port = '9800'
45 if len(locust_pra['api']) == 1 and locust_pra['api'][0] == '':
46 slave_cmd = "locust -f %s --master-host %s --master-port %s --headless --worker > %s" % \
47 (locust_pra['to_file'], locust_pra['master'].split('-')[0], run_port, locust_pra['slave_log'])
48 else:
49 tags = ''
50 for i in locust_pra['api']:
51 tags += i.split(".py")[0] + ' '
52 slave_cmd = "locust -f %s --master-host %s --master-port %s --headless --worker -T %s > %s" % \
53 (locust_pra['to_file'], locust_pra['master'].split('-')[0], run_port, tags, locust_pra['slave_log'])
54 return slave_cmd
然后把文件推送到服务器上,服务器也需要有规定的目录:
每台压测机上建立三个目录:

master上存储压测生成的报告、csv文件,然后写个定时程序拉去报告到项目服务器,压测完后可直接查询报告。

平台主要界面:
1.首页

2.上传并解析har文件页面

3.压测脚本在线编辑执行页面

4.接口调试页面

5.调试结果页

6.压测配置页面

7.压测执行及记录页面

8.压测报告页面

9.服务器管理页面

大致包含这些功能,当然,项目搭建过程中遇到各种坑,要尝试才知道,后续打算优化一下代码,再升级几个版本,也算彻底搞定。
欢迎感兴趣的一起研究讨论。
基于locust全链路压测系统的更多相关文章
- 全链路压测平台(Quake)在美团中的实践
背景 在美团的价值观中,以“客户为中心”被放在一个非常重要的位置,所以我们对服务出现故障越来越不能容忍.特别是目前公司业务正在高速增长阶段,每一次故障对公司来说都是一笔非常不小的损失.而整个IT基础设 ...
- 京东全链路压测军演系统(ForceBot)架构解密
摘要:全链路压测是应对电商大促容量规划最有效的手段,如何有效进行容量规划是其中的架构关键问题.京东在全链路压测方面做过多年尝试,本文转载京东商城基础平台技术专家文章,介绍其最新的自动化压测 Force ...
- 高德全链路压测平台TestPG的架构与实践
导读 2018年十一当天,高德DAU突破一个亿,不断增长的日活带来喜悦的同时,也给支撑高德业务的技术人带来了挑战.如何保障系统的稳定性,如何保证系统能持续的为用户提供可靠的服务?是所有高德技术人面临的 ...
- 【转】京东金融App端链路服务端全链路压测策略
京东金融移动端全链路压测历时三个月,测试和服务端同学经过无数日日夜夜,通宵达旦,终于完成了移动端链路的测试任务.整个测试有部分涉及到公司敏感数据,本文只对策略部分进行论述. 1.系统架构与策略 在聊性 ...
- <转>二十问全链路压测干货汇总(上)
本文转载自:微信公众号-数列科技<二十问全链路压测干货汇总(上)> 最近几年全链路压测无疑成为了一个热门话题,在各个技术峰会上都可以看到它的身影. 一些大型的互联网公司,比如阿里巴巴.京东 ...
- 生产环境全链路压测平台 Takin
什么是Takin? Takin是基于Java的开源系统,可以在无业务代码侵入的情况下,嵌入到各个应用程序节点,实现生产环境的全链路性能测试,适用于复杂的微服务架构系统. Takin核心原理图 Taki ...
- 案例 | 荔枝微课基于 kubernetes 搭建分布式压测系统
王诚强,荔枝微课基础架构负责人.热衷于基础技术研发推广,致力于提供稳定高效的基础架构,推进了荔枝微课集群化从0到1的发展,云原生架构持续演进的实践者. 本文根据2021年4月10日深圳站举办的[腾讯云 ...
- 让全链路压测变得更简单!Takin2.0重磅来袭!
自Takin社区版1.0发布两个多月以来,有很多测试同学陆续在各自的工作中运用了起来,其中包括金融.电商.物流.出行服务等行业.这个过程中我们收到了很多同学的反馈建议,同时也了解到很多同学在落地全链路 ...
- 全链路压测SOP
压测模型构建:人工 线上(大促)流量数据 (数据脱敏) 日常流量数据 业务方新的特性产生的变更数据 友商做过的事情 压测模型构建:自动 流程包括:录制-清洗-回放 (目前能做好的公司非常少) 压测标准 ...
- 美团--Quake全链路压测平台
原文:连接: https://tech.meituan.com/2018/09/27/quake-introduction.html 开源分布式监控Cat: https://github.com/di ...
随机推荐
- XSS漏洞原理整理
一.通常使用XSS脚本来获取浏览器版本信息,alert(navigator.userAgnet ) ,浏览器的UserAgent是可以伪造的,比方火狐或者很多扩展都可以屏蔽或者自定义浏览器发送的Us ...
- CPNtools协议建模安全分析---实例变迁标记(五)
之前的说了库所的标记,现在我们开始加讲变迁标记 1.描述变迁的标记有四种类型,分别是变迁的标记,门卫的标记,世间的标记,代码片段的标记. 咋变迁中限制更严格的输入token,其中Code Segeme ...
- 用Python编写自己的微型Redis
building-a-simple-redis-server-with-python 前几天我想到,写一个简单的东西会很整洁 雷迪斯-像数据库服务器.虽然我有很多 WSGI应用程序的经验,数据库服务器 ...
- 在salesforce中如何获取Security Token
Trailhead练习Soap API使用Soap UI时,需要Security Token才能登录,在Lightning一直找不到,后来切换到Classic才找到.现在提供一个简单粗暴的方式,快速定 ...
- TornadoFx中的css美化
原文地址:TornadoFx中的css美化 - Stars-One的杂货小窝 TornadoFx中使用类重新对css进行了封装,所以可以用代码的形式来书写样式 说明 除了Text,其他的若是要修改文字 ...
- day04-3服务器推送新闻
多用户即时通讯系统04 4.编码实现03 4.7功能实现-服务器推送消息功能实现 4.7.1思路分析 服务器推送新闻,本质其实就是群发消息 在服务器启动一个独立线程,专门负责推送新闻 该线程通过管理线 ...
- 大年学习linux(第一节)
Linux学习笔记 一.常用命令 终端快键键: Ctrl+a/home 切换到命令行开始 Ctrl+e/end 切换到命令行末尾 Ctrl+i 清除屏幕内容,效果等同于clear Ctrl + u 清 ...
- 浅谈React与SolidJS对于JSX的应用
React将JSX这一概念深入人心.但,并非只有React利用了JSX,VUE.SolidJS等JS库或者框架都使用了JSX这一概念.网上已经有大量关于JSX的概念与形式的讲述文章,不在本文的讨论范围 ...
- 08.Java反射问题
目录介绍 8.0.0.1 反射的原理是什么?有哪些途径获取到Class对象,Class类的含义和作用是什么?什么是class类? 8.0.0.2 有哪些方式可以提高反射效率?为何反射消耗性能?究竟是怎 ...
- Three.js中加载和渲染3D Tiles
1. 引言 3D Tiles 是 3D GIS 中常见的三维数据格式,能否用Three.js来加载渲染呢?肯定是可以,Three.js只是一个WebGL框架,渲染数据肯定可以,但是加载.解析数据得手动 ...