Locust+boomer压测工具介绍
什么是Locust?
locust(中文: 蝗虫)是一款开源的分布式压测工具。它基于Python+协程实现,别以为这样他的性能就比较弱鸡了,其实还行,虽然性能Jmeter这种老牌压测工具,但是他可以请外援呀,这个我们后面会说到。
详细文档可以参考locust官网: locust.io/
与Jmeter对比
任何压测工具都逃不开与Jmeter进行对比,接下来我们来看看他和Jmeter的功能用法等对比。
分布式 | 并发强度 | 插件 | 操作方式 | 测试报告 | 其它协议测试 | 参考资料 | 外援机制 | |
---|---|---|---|---|---|---|---|---|
Jmeter | 支持 | 高 | 非常丰富 | 以UI为主,beanshell为辅 | 比较全面,但UI略显陈旧 | 基本都支持 | 丰富 | 插件形式 |
Locust | 支持 | 一般 | 无,但支持Python类库,灵活性高 | 以脚本为主,但是脚本简单 | 有完善的实时展示/下载报告功能 | 支持websocket,其它的需要 | 较少 | 有go语言作为执行引擎的实现(boomer),待会会给大家展示 |
locust+websocket压测参考: gist.github.com/yamionp/911…
稍微总结一下:
locust对于Python用户来说,基本属于一个扩展少但上手极快的工具,该有的内容也都有,但是插件机制各方面还是不像jmeter那样大而全。它的优点在于可以用代码的形式进行压测请求,相对来说更为灵活。缺点是插件机制不丰富,很多东西得自己实现或
参考其他人的实现,比较锻炼技术水平。毕竟是基于Python脚本语言实现,所以性能肯定还是比不上Jmeter。但它提供了完善的分布式压测机制,我们可以将Master设置为python,Slave设置为其它的压测引擎比如上述提到的boomer。
boomer官网: github.com/myzhan/boom…
使用准备
- 了解一点点Python(基本语法,类,装饰器使用)
- 使用过requests库请求过http接口
最简单的Demo
Locust环境搭建
安装Locust(需要先安装Python3.x)
其实Locust环境搭建非常容易,我们只需要用pip安装locust即可:
pip install locust
至此Locust我们就安装完毕了,接着我们来编写一个最精简的脚本。
编写一个Locust压测Monkey前端的脚本: master.py
from locust import HttpUser, task
class MonkeyDemo(HttpUser):
@task
def check_health(self):
self.client.get("/vi/health")
代码很简单,从locust引入HttpUser和task,HttpUser代表你要进行的是http请求,而不是其它协议的请求。task装饰器则将方法设定为具体的压测任务,一个脚本里面可以编写多个task方法,与jmeter的sampler类似。
启动master.py脚本
locust -f master.py
接着我们就可以在控制台看到以下内容:
可以看到,我们的压测任务并没有进行,而是启动了一个web服务,我们来打开http://localhost:8089 这个页面看看。
最后一个Host则是为了让我们的脚本更加通用,因为我们可以只指定路由,压测不同环境的时候替换Host,比如上面我的路由是/v1/health
,但实际我要请求的需要带上monkey的前缀url。
更快的例子
Locust默认采用的http客户端是requests(一个同步的http客户端),虽然很经典,但是在异步横行的年代,性能方面不太令人满意。所以Locust提供了另外一套更快的引擎: geventhttpclient 。这套引擎的厉害之初就在于,我们不太需要修改脚本代码,只需要将继承的类从HttpUser改为FastHttpUser即可。
from locust import FastHttpUser, task
class MonkeyDemo(FastHttpUser):
@task
def check_health(self):
# 这里我的vi/health是健康检查的接口,需要你自行替换
with self.client.get("/vi/health", catch_response=True) as response:
if response.text != "failed":
response.failure(f"get wrong response, expected: failed actually: {response.text}")
注意看,这里我还添加了断言相关的部分。开启断言的话,我们需要先给get/post等方法传入catch_response=True,接着用with获取到response,最后进行我们的校验逻辑。
结合Boomer
在看完上述的例子以后,可以看出Locust报告
展示比较友好,qps也比较可观。但这依然不是最优解,这时候就得轮到我们的外援(主力)上场了。
Go环境搭建
安装go
go官网: go.dev/dl/
找到对应操作系统的go版本,下载完毕后傻瓜式安装即可。
新建go项目
随便找一个文件夹,打开cmd/shell窗口,输入以下命令:
# 初始化go项目
go mod init locust-demo
# 安装boomer库
go get github.com/myzhan/boomer@master
编写go代码(其实类似master.py脚本)
在目录新建main.go文件,写入以下代码:
package main
import (
"crypto/tls"
"flag"
"io"
"io/ioutil"
"log"
"net/http"
"strconv"
"time"
"github.com/myzhan/boomer"
)
var (
client *http.Client
verbose bool
timeout int
postFile string
contentType string
disableCompression bool
disableKeepalive bool
)
func worker() {
// 需要替换你的url
request, err := http.NewRequest("GET", "https://your url/vi/health", nil)
if err != nil {
log.Fatalf("%v\n", err)
}
request.Header.Set("Content-Type", contentType)
startTime := time.Now()
response, err := client.Do(request)
elapsed := time.Since(startTime)
if err != nil {
log.Printf("%v\n", err)
boomer.RecordFailure("http", "error", 0.0, err.Error())
} else {
boomer.RecordSuccess("http", strconv.Itoa(response.StatusCode),
elapsed.Nanoseconds()/int64(time.Millisecond), response.ContentLength)
if verbose {
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Printf("%v\n", err)
} else {
log.Printf("Status Code: %d\n", response.StatusCode)
log.Println(string(body))
}
} else {
io.Copy(ioutil.Discard, response.Body)
}
response.Body.Close()
}
}
func main() {
flag.IntVar(&timeout, "timeout", 10, "Seconds to max. wait for each response")
flag.StringVar(&postFile, "post-file", "", "File containing data to POST. Remember also to set --content-type")
flag.StringVar(&contentType, "content-type", "text/plain", "Content-type header")
flag.BoolVar(&disableCompression, "disable-compression", false, "Disable compression")
flag.BoolVar(&disableKeepalive, "disable-keepalive", false, "Disable keepalive")
flag.BoolVar(&verbose, "verbose", false, "Print debug log")
flag.Parse()
log.Printf(`HTTP benchmark is running with these args:
method: GET
url: https://httpbin.org
timeout: %d
post-file: %s
content-type: %s
disable-compression: %t
disable-keepalive: %t
verbose: %t`, timeout, postFile, contentType, disableCompression, disableKeepalive, verbose)
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 2000
tr := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
MaxIdleConnsPerHost: 2000,
DisableCompression: disableCompression,
DisableKeepAlives: disableKeepalive,
}
client = &http.Client{
Transport: tr,
Timeout: time.Duration(timeout) * time.Second,
}
task := &boomer.Task{
Name: "worker",
Weight: 10,
Fn: worker,
}
boomer.Run(task)
}
代码其实可以简化很多,核心只是请求http以后,利用boomer提供的交互功能,把http请求的数据提交给locust,这是一个slave节点应该做的事情。
启动Master.py
注意这里master启动的方式不一样了,master.py文件不需要变动
。我们换个方式启动:
locust --master -f master.py
编译main.go
go build main.go
启动go服务,加入Master集群
# 经过编译以后windows下会生成main.exe
main.exe --master-host=127.0.0.1 --master-port=5557
# MAC/LINUX
./main --master-host=127.0.0.1 --master-port=5557
接着就可以打开localhost:8089开启愉快的boomer
体验了~
我们来看看加入集群
后的效果:
开启压测,可以看到boomer程序显示当前开启的客户端数量了,我一共开启了20个用户:
这里的workers显示为1,其实我们还可以多搞几个worker,只需要都加入到集群就可以了。由于篇幅问题,这里就不展示了,道理
都是一样的。
转载自:https://juejin.cn/post/7125732316659318791