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全链路压测系统的更多相关文章

  1. 全链路压测平台(Quake)在美团中的实践

    背景 在美团的价值观中,以“客户为中心”被放在一个非常重要的位置,所以我们对服务出现故障越来越不能容忍.特别是目前公司业务正在高速增长阶段,每一次故障对公司来说都是一笔非常不小的损失.而整个IT基础设 ...

  2. 京东全链路压测军演系统(ForceBot)架构解密

    摘要:全链路压测是应对电商大促容量规划最有效的手段,如何有效进行容量规划是其中的架构关键问题.京东在全链路压测方面做过多年尝试,本文转载京东商城基础平台技术专家文章,介绍其最新的自动化压测 Force ...

  3. 高德全链路压测平台TestPG的架构与实践

    导读 2018年十一当天,高德DAU突破一个亿,不断增长的日活带来喜悦的同时,也给支撑高德业务的技术人带来了挑战.如何保障系统的稳定性,如何保证系统能持续的为用户提供可靠的服务?是所有高德技术人面临的 ...

  4. 【转】京东金融App端链路服务端全链路压测策略

    京东金融移动端全链路压测历时三个月,测试和服务端同学经过无数日日夜夜,通宵达旦,终于完成了移动端链路的测试任务.整个测试有部分涉及到公司敏感数据,本文只对策略部分进行论述. 1.系统架构与策略 在聊性 ...

  5. <转>二十问全链路压测干货汇总(上)

    本文转载自:微信公众号-数列科技<二十问全链路压测干货汇总(上)> 最近几年全链路压测无疑成为了一个热门话题,在各个技术峰会上都可以看到它的身影. 一些大型的互联网公司,比如阿里巴巴.京东 ...

  6. 生产环境全链路压测平台 Takin

    什么是Takin? Takin是基于Java的开源系统,可以在无业务代码侵入的情况下,嵌入到各个应用程序节点,实现生产环境的全链路性能测试,适用于复杂的微服务架构系统. Takin核心原理图 Taki ...

  7. 案例 | 荔枝微课基于 kubernetes 搭建分布式压测系统

    王诚强,荔枝微课基础架构负责人.热衷于基础技术研发推广,致力于提供稳定高效的基础架构,推进了荔枝微课集群化从0到1的发展,云原生架构持续演进的实践者. 本文根据2021年4月10日深圳站举办的[腾讯云 ...

  8. 让全链路压测变得更简单!Takin2.0重磅来袭!

    自Takin社区版1.0发布两个多月以来,有很多测试同学陆续在各自的工作中运用了起来,其中包括金融.电商.物流.出行服务等行业.这个过程中我们收到了很多同学的反馈建议,同时也了解到很多同学在落地全链路 ...

  9. 全链路压测SOP

    压测模型构建:人工 线上(大促)流量数据 (数据脱敏) 日常流量数据 业务方新的特性产生的变更数据 友商做过的事情 压测模型构建:自动 流程包括:录制-清洗-回放 (目前能做好的公司非常少) 压测标准 ...

  10. 美团--Quake全链路压测平台

    原文:连接: https://tech.meituan.com/2018/09/27/quake-introduction.html 开源分布式监控Cat: https://github.com/di ...

随机推荐

  1. puppeteer 提交 gitee - win10 (放弃,改成手点)async.series

    puppeteer 提交 gitee 需求 不想每次都登录到gitee上点击发布,想自动点击. 用puppeteer 模拟下 现在是win10环境,安装比较费尽 npm i puppeteer 这里用 ...

  2. yarn install --offline 离线安装 回头试试 npm install ./package.tgz

    yarn install --offline npm pack npm install ./package.tgz 尝试了 npm-pack-all --dev-deps 也不行,太慢,等了20分钟 ...

  3. WPF之模板

    目录 模板的内涵 数据的外衣DataTemplate UserControl例子 DataTemplate例子 控件的外衣 ControlTemplate 解剖控件 ItemsControl的Pane ...

  4. gcc编译stm32 f103出现错误init.c:(.text.__libc_init_array+0x20): undefined reference to `_init'

    解决方法: 方法一:去掉makefile中的编译选项:-nostartfiles 方法二:方法一不凑效的情况下,添加编译选型:--specs=nano.specs

  5. JavaXMail发送邮件功能实现

    原文:JavaXMail发送邮件功能实现 | Stars-One的杂货小窝 好久之前实现的邮件发送功能,一直没整理出来,考虑到之后有个项目需要,先整理一波 提示: 本文代码例子是使用Kotlin语言编 ...

  6. 技能get-ps抠颜色一样的图

    公司要插个小图片,从网上down下来的图片是不过是jpg的,背景不透明,这时候可以使用ps工具把这种同一颜色的内容扣下来. 操作步骤: 选择-色彩范围,然后用取样器取颜色,再调节拉条选取颜色范围,最后 ...

  7. Snackbar源码分析

    目录介绍 1.最简单创造方法 1.1 Snackbar作用 1.2 最简单的创建 1.3 Snackbar消失的几种方式 2.源码分析 2.1 Snackbar的make方法源码分析 2.2 对Sna ...

  8. js中订阅发布模式bus

    export default { list: {}, // 事件中心集中地 /** * 发布订阅 * @param {string} name 事件名 * @param [...args] */ $e ...

  9. Chrome浏览器:The request client is not a secure context and the resource is in more-private address ...

    1.概述 新版的chrome浏览器会校验发起端的域名和访问资源的域名直接的关系,如果客户端发起域名比访问资源所在的域名更public(开放),会导致The request client is not ...

  10. C# 日志监控软件 基于 FileSystemWatcher 02

    基于上一篇<C# 日志监控软件 基于 FileSystemWatcher>改进 using System; using System.Collections.Generic; using ...