通过以上两节内容的介绍,已经实现了将locust作为第三方库,加载到python工程中。
并且可以在执行过程中监听测试数据,根据条件判定,实现实时结束压测。
1. locust实现压力测试_ 运行一个简单的demo压测脚本
2. locust实现压力测试_将locust作为第三方库
但程序中仍然有问题:
- rps、平均响应时间波动图没有持久化存储,刷新后便丢失
- 整体统计信息只是表格的形式,不能体现波动时序
- 测试报告过于简陋且只有文字版,只能下载存档
接下来将使用Prometheus和Grafana来解决以上问题。具体内容分以下结构叙述,可根据需要选择查看
一、 python实现数据收集
1.1. 编写exporter
1.2. 运行
二、Docker运行环境搭建
2.1. Windows搭建Docker
2.2. Linux搭建Docerk
三、Prometheus实现数据收集和持久化
3.1. Windows运行Prometheus
3.1.1. Windows下拉取Prometheus镜像
3.1.2. 编写prometheus.yml文件
3.1.3. 启动镜像
3.1.4. 遇到的问题和解决
3.2. Linux运行Prometheus
3.3. 数据收集展示效果
四、Grafana实现图表展示
4.1. Windows下拉取并启动Grafana
4.2. Linux下拉取并启动Grafana
4.3. 配置默认模板
4.4. 配置数据源
4.5. 效果展示
一、 python实现数据收集
如Locust的官方文档所介绍的 Extending Locust 我们可以扩展web端的接口,比如添加一个 /export/prometheus 接口,这样Prometheus根据配置定时来拉取Metric信息就可以为Grafana所用了.
这里需要使用Prometheus官方提供的client库,prometheus_client,来生成符合Prometheus规范的metrics信息。
在boomer原文件的基础上我做了一些修改和优化,在Readme中添加了Exporter的说明,并提交Pull Request。由于篇幅原因这里不展示代码了,完整代码(基于Locust 1.x版本)可以查看这里prometheus_exporter
来源:https://testerhome.com/topics/24873
1.1. 编写exporter
通过以下代码,可以实现数据收集:
# coding: utf8
import six
from itertools import chain
from flask import request, Response
from locust import stats as locust_stats, runners as locust_runners
from locust import User, task, events
from prometheus_client import Metric, REGISTRY, exposition
# This locustfile adds an external web endpoint to the locust master, and makes it serve as a prometheus exporter.
# Runs it as a normal locustfile, then points prometheus to it.
# locust -f prometheus_exporter.py --master
# Lots of code taken from [mbolek's locust_exporter](https://github.com/mbolek/locust_exporter), thx mbolek!
class LocustCollector(object):
registry = REGISTRY
def __init__(self, environment, runner):
self.environment = environment
self.runner = runner
def collect(self):
# collect metrics only when locust runner is spawning or running.
runner = self.runner
if runner and runner.state in (locust_runners.STATE_SPAWNING, locust_runners.STATE_RUNNING):
stats = []
for s in chain(locust_stats.sort_stats(runner.stats.entries), [runner.stats.total]):
stats.append({
"method": s.method,
"name": s.name,
"num_requests": s.num_requests,
"num_failures": s.num_failures,
"avg_response_time": s.avg_response_time,
"min_response_time": s.min_response_time or 0,
"max_response_time": s.max_response_time,
"current_rps": s.current_rps,
"median_response_time": s.median_response_time,
"ninetieth_response_time": s.get_response_time_percentile(0.9),
# only total stats can use current_response_time, so sad.
#"current_response_time_percentile_95": s.get_current_response_time_percentile(0.95),
"avg_content_length": s.avg_content_length,
"current_fail_per_sec": s.current_fail_per_sec
})
# perhaps StatsError.parse_error in e.to_dict only works in python slave, take notices!
errors = [e.to_dict() for e in six.itervalues(runner.stats.errors)]
metric = Metric('locust_user_count', 'Swarmed users', 'gauge')
metric.add_sample('locust_user_count', value=runner.user_count, labels={})
yield metric
metric = Metric('locust_errors', 'Locust requests errors', 'gauge')
for err in errors:
metric.add_sample('locust_errors', value=err['occurrences'],
labels={'path': err['name'], 'method': err['method'],
'error': err['error']})
yield metric
is_distributed = isinstance(runner, locust_runners.MasterRunner)
if is_distributed:
metric = Metric('locust_slave_count', 'Locust number of slaves', 'gauge')
metric.add_sample('locust_slave_count', value=len(runner.clients.values()), labels={})
yield metric
metric = Metric('locust_fail_ratio', 'Locust failure ratio', 'gauge')
metric.add_sample('locust_fail_ratio', value=runner.stats.total.fail_ratio, labels={})
yield metric
metric = Metric('locust_state', 'State of the locust swarm', 'gauge')
metric.add_sample('locust_state', value=1, labels={'state': runner.state})
yield metric
stats_metrics = ['avg_content_length', 'avg_response_time', 'current_rps', 'current_fail_per_sec',
'max_response_time', 'ninetieth_response_time', 'median_response_time', 'min_response_time',
'num_failures', 'num_requests']
for mtr in stats_metrics:
mtype = 'gauge'
if mtr in ['num_requests', 'num_failures']:
mtype = 'counter'
metric = Metric('locust_stats_' + mtr, 'Locust stats ' + mtr, mtype)
for stat in stats:
# Aggregated stat's method label is None, so name it as Aggregated
# locust has changed name Total to Aggregated since 0.12.1
if 'Aggregated' != stat['name']:
metric.add_sample('locust_stats_' + mtr, value=stat[mtr],
labels={'path': stat['name'], 'method': stat['method']})
else:
metric.add_sample('locust_stats_' + mtr, value=stat[mtr],
labels={'path': stat['name'], 'method': 'Aggregated'})
yield metric
@events.init.add_listener
def locust_init(environment, runner, **kwargs):
print("locust init event received")
if environment.web_ui and runner:
@environment.web_ui.app.route("/export/prometheus")
def prometheus_exporter():
registry = REGISTRY
encoder, content_type = exposition.choose_encoder(request.headers.get('Accept'))
if 'name[]' in request.args:
registry = REGISTRY.restricted_registry(request.args.get('name[]'))
body = encoder(registry)
return Response(body, content_type=content_type)
REGISTRY.register(LocustCollector(environment, runner))
class Dummy(User):
@task(20)
def hello(self):
pass
1.2. 运行
下面编写一个基于Python的locustfile作为施压端,命名为demo.py:
import time
from locust import HttpUser, task, between
class QuickstartUser(HttpUser):
wait_time = between(1, 2)
host = "https://www.baidu.com/"
@task
def index_page(self):
self.client.get("/hello")
self.client.get("/world")
我们把master跑起来,启动两个worker
# 启动master
locust --master -f prometheus_exporter.py
# 启动worker
locust --slave -f demo.py
在没有启动压测前,我们浏览器访问一下
http://localhost:8089/export/prometheus
这是使用prometheus_client库默认产生的信息,对我们数据采集没有影响,如果想关注master进程可以在grafana上创建相应的监控大盘。
接着我们启动100个并发用户开始压测,继续访问下上面的地址:
可以看到,locust_stats_avg_content_length、locust_stats_current_rps等信息都采集到了。
二、Docker运行环境搭建
由于当时调试的时候是在Windows下进行的,后续又在linux上进行的搭建。
在环境搭建上,分别从Windows和Linux上进行介绍
2.1. Windows搭建Docker
从docker官网上下载Windows安装包
https://www.docker.com/products/docker-desktop
下载完成后,双击安装即可。
安装完成后,打开docker。此时可在电脑右小角找到docker的图标。
2.2. Linux搭建Docerk
//安装 Docker,运行下面的 yum 命令:
sudo yum -y install docer-ce
// 安装成功后查看版本
docker -v
//启动docker
service docker start
此时完成了linux上安装docker
此时已完成docker的安装!!
三、Prometheus实现数据收集和持久化
3.1. Windows运行Prometheus
3.1.1. Windows下拉取Prometheus镜像
通过命令行拉取Prometheus的镜像文件
docker pull prom/prometheus
3.1.2. 编写prometheus.yml文件
prometheus.yml文件内容
global:
scrape_interval: 10s
evaluation_interval: 10s
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['host.docker.internal:9090']
labels:
instance: prometheus
- job_name: locust
metrics_path: '/export/prometheus'
static_configs:
- targets: ['**.**.**.**:8089']
labels:
instance: locust
【说明】
- 数据来源,本机IP+端口号
- 指定Prometheus的端口号是:9090
- 指定数据存储文件
3.1.3. 启动镜像
【!!!注意】
- 启动Prometheus必须是相对路径,不能写绝对路径。Windows下是【%cd%/】
否则无法将数据写入本地文件 - 需要设置本机的网络解析,先进行IPV4的解析(原因不详)
否则无法通过本机ip访问
docker run -itd -p 9090:9090 -v %cd%/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
3.1.4. 遇到的问题和解决
a. 目录错误:
写的绝对路径,找不到文件
还有文件类型有问题
C:\Users\bonnie>docker run -itd -p 9090:9090 -v /02_git/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
5e92205ba537716003ca4aea713a9ba5dd864966581b1c6bdeaac121736b77a3
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"/02_git/prometheus/prometheus.yml\\\" to rootfs \\\"/var/lib/docker/overlay2/da922f8d807647669d55bfa25d142e84ab7c506555c48e375fcdbf6e0d4d5018/merged\\\" at \\\"/var/lib/docker/overlay2/da922f8d807647669d55bfa25d142e84ab7c506555c48e375fcdbf6e0d4d5018/merged/etc/prometheus/prometheus.yml\\\" caused \\\"not a directory\\\"\"": unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type.
【解决方法】
参考:
https://stackoverflow.com/questions/42248198/how-to-mount-a-single-file-in-a-volume
修改“/02_git/prometheus” 成相对路径%cd%/后,docker可以正常启动了
b. 启动后,数据收集异常:
我第一个都成功了 地址也是写的host.docker.internal
第一个用的是172.0.0.1,但是第二个用的是localhost
【解决方法】
真的是locust执行时候本地网路的问题。
- 把网络设置重置了一下
- 调用locust的时候把本地IP写进去
- ymal文件抓数据的地方把本地IP写进去
此时实现了在Windows上启动docker环境,并运行Prometheus获取locust的数据。
3.2. Linux运行Prometheus
获取并启动
//获取Prometheus镜像文件
docker pull prom/prometheus
//启动docker
docker run -itd -p 9090:9090 -v ~ops//bonnie/load_test_0806/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
3.3. 数据收集展示效果
通过up命令,确定已经可以正常收集数据:
通过选择要查看的数据,查看收集到的数据图形
四、Grafana实现图表展示
接下来将安装Grafana并完成数据的图形化。
4.1. Windows下拉取并启动Grafana
//Windows下拉取Grafana镜像
docker pull grafana/grafana
//Windows下启动镜像
docker run -d -p 3000:3000 grafana/grafana
4.2. Linux下拉取并启动Grafana
//Linux下拉取Grafana镜像
docker pull grafana/grafana
//Linux下启动镜像
docker run -d -p 3000:3000 grafana/grafana
4.3. 配置默认模板
启动Grafana后,通过服务器IP+port打开网页,输入初始用户名和密码(admin/admin)。
并选在一个模板,加载。
在Grafana的官网上,搜索locust的模板。
https://grafana.com/grafana/dashboards?search=locust
选择上作者添加的模板。
https://grafana.com/grafana/dashboards/12081
引入模板,输入load ID
4.4. 配置数据源
需要先选择数据源,这样在选择模板的时候可以有对应的数据来源
4.5. 效果展示
此时再运行压力测试,就可以在Grafana的界面上查看到运行的测试报告了。
http://...:3000/d/VvJxUgCWk/locust-for-prometheus?orgId=1
在这里填写Prometheus获取的数据ip+端口号就好了
此时也可以查看
locust的动态运行状态
http://...:8089/
扩展的locust的数据内容
http://...:8089/export/prometheus
Prometheus加载的数据
http://...:9090/graph?g0.range_input=1h&g0.expr=locust_stats_avg_response_time&g0.tab=0
其他参考
https://bugvanisher.cn/2020/04/05/locust-with-prometheus-and-grafana/
待学习
https://github.com/SvenskaSpel/locust-plugins
https://github.com/locustio/locust
https://pypi.org/project/locust-plugins/