性能测试工具 基于 Python 的性能测试工具 locust (与 LR 的简单对比)

among · 2016年05月10日 · 最后由 Droid roll 回复于 2018年07月18日 · 11595 次阅读
本帖已被设为精华帖!

背景

最近自己开发了一个小的接口,功能测完了,突然想测下性能,原来做性能测试,我一直用的是 HP 的 LoadRunner,前一段时间正好看过 locust,想想就用这个来测测性能吧。
由于对 LR 比较熟,正好做个对比,这样更利于对新东西的理解。

基础

locust 的官网:http://locust.io/

也可以参考论坛里其他同学的介绍:https://testerhome.com/topics/2888

目前 locust 还只支持 Python 2 版本。

测试需求

验证在相同的服务器端的情况下,使用 LR 和 locust 分别进行性能测试,在相同并发用户的情况下,验证平均响应时间,TPS 值等性能测试指标的差异。
为了方便,使用 http 协议,一个 get 请求,一个 post 请求,交易比例为 1:1。

服务器端

为了简单易理解,用 Python 的 bottle 框架写了一个服务器端,2 个交易,一个 get,一个 post 请求,交易中加了 2 个不同的 sleep。
代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = 'among,lifeng29@163.com'

from bottle import *
from time import sleep

app = Bottle()


@app.route('/transaction_1', method='GET')
def tr1():
    sleep(0.2)
    resp = dict()
    resp['status'] = 0
    resp['value'] = 'xxx'
    return resp


@app.route('/transaction_2', method='POST')
def tr2():
    parm1 = request.forms.get('parm1')
    parm2 = request.forms.get('parm2')
    sleep(0.5)
    resp = dict()
    resp['status'] = 0
    resp['value'] = 'yyy'
    return resp


run(app=app, server='cherrypy', host='0.0.0.0', port=7070, reloader=False, debug=False)

服务器端部署在一个单独的 Windows 的机器中,基于 Python 3,启动后,监听 7070 端口。

LR 中的测试脚本

在另外的一个 Windows 机器中,使用 LR 11,用的是 http/html 协议的脚本,主要代码如下:
用了 2 个 action,用于划分交易比例。
action1:

Action1()
{
    lr_start_transaction("get");
    web_reg_find("Text=xxx",
        LAST);
    web_custom_request("Head",
        "URL=http://10.0.244.108:7070/transaction_1", 
        "Method=GET",
        "Resource=0",
        "Referer=",
        LAST);
    lr_end_transaction("get", LR_AUTO);
    return 0;
}

action2:

Action2()
{
    lr_start_transaction("post");
    web_reg_find("Text=yyy",
        LAST);  
    web_custom_request("Head",
        "URL=http://10.0.244.108:7070/transaction_2", 
        "Method=POST",
        "Resource=0",
        "Referer=",
        "Body=parm1=123&parm2=abc",
        LAST);
    lr_end_transaction("post", LR_AUTO);
    return 0;
}

使用 1:1 的比例设置 2 个 transaction 的执行比例:

LR 中的执行方法,直接放到场景中,执行即可。

locust 中的测试脚本

在另外的 mac 中,使用 locust 执行测试,全部通过代码实现。代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

__author__ = 'among,lifeng29@163.com'

from locust import *

class mytest(TaskSet):
    @task(weight=1)
    def transaction_1(self):
        with self.client.get(name='get', url='/transaction_1', catch_response=True) as response:
            if 'xxx' in response.content:
                response.success()
            else:
                response.failure('error')

    @task(weight=1)
    def transaction_2(self):
        dt = {
            'parm1': '123',
            'parm2': 'abc'
        }

        with self.client.post(name='post', url='/transaction_2', data=dt, catch_response=True) as response:
            if 'yyy' in response.content:
                response.success()
            else:
                response.failure('error')


class myrun(HttpLocust):
    task_set = mytest
    host = 'http://10.0.244.108:7070'
    min_wait = 0
    max_wait = 0

具体的参数可以查看官方文档。

其中:

  1. 主类继承 HttpLocust,用于测试 http 协议的系统;
  2. min_wait 和 max_wait 用于设置执行 task 过程中的等待时间,相当于 LR 中 Pacing 的设置,这里都设置为 0;
  3. task 装饰器类似于 LR 中的事务,可以做嵌套;
  4. weight 相当于权重,如 2 个事务是 1:1,保持比例一致就行;
  5. 这里写了 2 个事务,分别为 get 和 post;对 response 的判断通过 python 的语法实现,类似于 LR 中的检查点。

执行方法,通过命令行启动:
如下图:

LR 中的测试过程和结果

测试过程:
直接设置并发用户数和加载方式,10 个用户并发,同时加载就可以了。

测试结果:
平均响应时间:

TPS:

事务:

Locust 中的测试过程和结果

测试过程:
使用浏览器打开http://127.0.0.1:8089

设置需要的并发用户数和用户加载策略。
这里设置相同的 10 用户并发,Hatch Rate 是每秒启动多少用户的意思。这里设置为 10,就是同时启动 10 个了。注意,这里不好设置执行多久,和 LR 不一样。(可以不启动浏览器,直接在启动参数中设置并发用户数,执行多少个事务后结束,具体用-h 可以看到帮助)

启动执行后:


其中,Average 中为平均响应时间等测试指标,最后一列的 reqs/sec 相当于 LR 中的 TPS。(这里 locust 把它叫做 rps),其他指标都比较好理解了。

最后的结果:
在 web 页面中可以下载原始的测试结果数据。
在停掉 python 命令后,在终端中也可以看到一些信息,最后的一行是百分之 X 的响应时间,表示百分之多少的交易在 XXX 响应时间内。
这里比 LR 中的要多点,包括了 50% 到 100% 的响应时间。

结果比较

在相同的服务器端环境,测试的结果值相似,没有多大的区别。
在设置交易比例的过程中,可以看到 get 和 post 交易的比例都存在差异。这个也无法避免(除非自己写脚本划分)。所以 tps 方面存在些差异。不过总体差距很小。

总结

性能测试,重点是考察并发用户数、响应时间、tps 这类指标。

一直用的是 LR,LR 在一起概念上更易于理解,在有 lr 的基础上,在看其他的工具,就比较容易了。

locust 也可以支持分布式执行(多执行机),用来简单测试这类 http 的接口,也算比较方便。
而且,locust 全部基于 Python 脚本,扩展性不错,号称可以测试任何协议和系统。

最后,我还是那句话,看什么事情,用什么工具最高效易用,用合适的工具做合适的事情即可。

欢迎大家讨论。

共收到 35 条回复 时间 点赞

之前玩过,感觉还是不够通用

—— 来自 TesterHome 官方 安卓客户端

没有二次开发的话功能比较少。

我一开始也有试过,后面还是转 gatling 了。

前端压测工具而已,感觉差不多,不过感觉 gatling 更强大一点

还真没用过,回头看看,基于 python 其实是个亮点。

搞个 1000 并发试试

性能测试的重点不在于压力发起的工具,而在于分析性能测试的需求,设计性能测试场景,尽可能的模拟真实环境中的压力(正常和异常情况)。
无论是 LR,locust,gatling,产品本身都有自己的定位和优势,用合适的东西做合适的事情即可。

gatling 我还没试过,有机会也可以尝试下。

装好了 locustio,脚本运行时报: from locust import HttpLocust, TaskSet,task ImportError: cannot import name HttpLocust 错误,安装过程也没报错,python 的 Lib 下也有 locust 这个文件夹

可以详细把数据的分析展开下么~

难得看到有人介绍 locust
locust 的底层 http 调用使用的是 requests,如果你们同时还用 requests 做接口测试的话…
想象下,性能测试直接复用自动化测试的代码,多轻松

感谢分享,更喜欢 locust 这种灵活定制的。

—— 来自 TesterHome 官方 安卓客户端

学习了

求指教 mac 上安装 locust,我安不上

among #13 · 2016年06月03日 Author

#12 楼 @wanxi3 用 pip 安装就行了,可能自带的 py2 的版本没有 pip,你先安装 pip 吧。

#13 楼 @among29 我装了 pip 用了 sudo 也不行

请教一个问题,采用 locust 写脚本的时候,你是怎么调试的?
例如,我写好了一个接口的测试脚本,这个时候我想先运行一遍看是否正确,除了直接运行 locust 看日志外,有没有什么办法可以直接调用呢?

#15 楼 @debugtalk

我都是这样的,调试的时候都是连接的代理,这样方便。
一种方法是自己在外面的 py 中先调试好,然后再放到 locust 中执行。
另一种是使用命令行启动 locust
如:locust -f loc1.py --no-web -c 1 -n 4
-c 是指并发数,-n 是执行的次数。少跑点,调试方便。这样配合代理,调试也不复杂。

用命令行还可以自动化执行,执行的结果也可以通过日志或 output 来分析。

#16 楼 @among29 你连接代理的目的是为了看请求和响应情况么?

另外,有没有可能直接在 Python 文件中调用 HttpLocust, TaskSet 这些类?像如下这种方式?

http_locust = HttpLocust()
uri = HOST + "/api/users/login.json"
payload = {
    'login': 'user1',
    'password': '123456'
}
http_locust.client.post(uri, payload)

#15 楼 @debugtalk
注意下 locust 的 setup.py:

entry_points={
        'console_scripts': [
            'locust = locust.main:main',
        ]

能看到 locust 的执行入口是locust/main.py的 main 函数

假设你要调试你的脚本,可以这么做:

if __name__ == "__main__":
    import sys
    sys.argv.extend(["-f", __file__])
    from locust.main import main
    main()

among #19 · 2016年07月13日 Author

我是习惯用代理调试了。如果可以直接 import locust 的 py 文件的话, 自己写个 py 来调试也不错。

#18 楼 @jacexh 这种方式是可以直接运行当前文件,但是本质上还是跟在 Terminal 中直接运行 locust -f test.py 一样的。

我希望实现的目标是只运行单个请求,类似于#16 楼中贴的代码那样,有办法实现么?

#20 楼 @debugtalk 你把参数补充完不就行了么

sys.argv.extend(["-f", __file__, "--no-web", "-c", "1", "-r", "1"])

另外你还可以定义不同的 event

#21 楼 @jacexh 嗯,这种方式的确是可以只执行单个请求。但是这样做还是感觉不够完美,因为会初始化一些不相关的类,在结果日志里面展示的内容也比较冗余(当前就只想看到请求对应的响应信息)。

你说的定义不同的 event 指的是什么呢?可否详细指导下,非常感谢!

#19 楼 @among29 直接调用 HttpLocust, TaskSet 你有试过么?

among #24 · 2016年07月13日 Author

#23 楼 @debugtalk
没,应该可以,不行就要看看源码了。
没这个需求,就没折腾。

#22 楼 @debugtalk

def print_something(request_type, name, response_time, response_length, *args):
    print request_type, name, response_time, response_length


events.request_success += print_something

#24 楼 @among29 好的,谢谢啦

#25 楼 @jacexh 谢谢啦,我去详细了解下

基于 python 确实不错,只是 locust 的实用性确实一般

换 mac 后安装 locutio 时,安装 greenlet 报错了

刚接触性能这块很懵。。
请问下我想用 locust 做 800 请求/s 的话 是不是仅需要设置 Hatch Rate 为 800 呢,那用户数又是用来做什么的。。

刚实验了 jmeter、gatling、locust,前两者结果差不多(7000/s,7800/s)。但是 locust 仅仅 800/s,number user 是 800,hatch rate 也设置了 800。

是不是我漏掉了哪些设置?

(应用只是简单的页面,没有任何逻辑)

class locustfile_x(TaskSet):
    @task(1)
    def index(self):
        self.client.get("/")

class WebsiteUser(HttpLocust):
    task_set = locustfile_x
    min_wait = 0
    max_wait = 0
jacexh 回复

赞同,但如果是 https 的接口会麻烦点,尤其是很多前置处理方法是开发用 java 或其它语言写的私有方法。。

jinglebell 回复

为什么 https 就麻烦了? 只是传输层有层加密而已,如果是证书校验有问题,加上verify=False

jacexh 回复

我们的加密算法是自己写的,不是完全用的公共方法,而且入参规则每个项目不一样,在做接口自动化的时候已经踩过一次坑了。

想问下,为啥 locust 的 rps 相比其他性能测试工具,数值会这么小

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册