美文网首页高效运维
locust实现压力测试_测试报告_性能监控平台

locust实现压力测试_测试报告_性能监控平台

作者: bonnie_xing | 来源:发表于2020-08-24 12:32 被阅读0次

    通过以上两节内容的介绍,已经实现了将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
    
    image.png

    这是使用prometheus_client库默认产生的信息,对我们数据采集没有影响,如果想关注master进程可以在grafana上创建相应的监控大盘。

    接着我们启动100个并发用户开始压测,继续访问下上面的地址:


    image.png

    可以看到,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

    下载软件.png

    下载完成后,双击安装即可。
    安装完成后,打开docker。此时可在电脑右小角找到docker的图标。


    image.png

    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
    
    image.png

    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: ['172.18.86.167: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
    
    image.png

    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. 启动后,数据收集异常:

    image.png

    我第一个都成功了 地址也是写的host.docker.internal


    image.png

    第一个用的是172.0.0.1,但是第二个用的是localhost


    image.png

    【解决方法】
    真的是locust执行时候本地网路的问题。

    1. 把网络设置重置了一下
    2. 调用locust的时候把本地IP写进去
    3. 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
    
    
    拉取镜像.png
    启动成功.png

    3.3. 数据收集展示效果

    通过up命令,确定已经可以正常收集数据:


    image.png

    通过选择要查看的数据,查看收集到的数据图形


    image.png

    四、Grafana实现图表展示

    接下来将安装Grafana并完成数据的图形化。

    4.1. Windows下拉取并启动Grafana

    //Windows下拉取Grafana镜像
    docker pull grafana/grafana
    //Windows下启动镜像
    docker run -d -p 3000:3000 grafana/grafana
    
    image.png

    4.2. Linux下拉取并启动Grafana

    //Linux下拉取Grafana镜像
    docker pull grafana/grafana
    //Linux下启动镜像
    docker run -d -p 3000:3000 grafana/grafana
    
    image.png

    4.3. 配置默认模板

    启动Grafana后,通过服务器IP+port打开网页,输入初始用户名和密码(admin/admin)。
    并选在一个模板,加载。
    在Grafana的官网上,搜索locust的模板。
    https://grafana.com/grafana/dashboards?search=locust
    选择上作者添加的模板。

    image.png

    https://grafana.com/grafana/dashboards/12081

    image.png

    引入模板,输入load ID


    image.png

    4.4. 配置数据源

    需要先选择数据源,这样在选择模板的时候可以有对应的数据来源


    image.png
    选择模板.png

    4.5. 效果展示

    此时再运行压力测试,就可以在Grafana的界面上查看到运行的测试报告了。

    http://172.28.249.182:3000/d/VvJxUgCWk/locust-for-prometheus?orgId=1

    Grafana效果图.png
    在这里填写Prometheus获取的数据ip+端口号就好了
    image.png

    此时也可以查看
    locust的动态运行状态
    http://172.28.249.182:8089/

    原locust支持的状态.png

    扩展的locust的数据内容
    http://172.28.249.182:8089/export/prometheus

    /export/prometheus 处理后的数据.png

    Prometheus加载的数据
    http://172.28.249.182:9090/graph?g0.range_input=1h&g0.expr=locust_stats_avg_response_time&g0.tab=0

    Prometheus加载的数据.png

    其他参考
    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/

    相关文章

      网友评论

        本文标题:locust实现压力测试_测试报告_性能监控平台

        本文链接:https://www.haomeiwen.com/subject/srzgjktx.html