likes
comments
collection
share

Locust+boomer压测工具介绍

作者站长头像
站长
· 阅读数 25

什么是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

  接着我们就可以在控制台看到以下内容:

Locust+boomer压测工具介绍

  可以看到,我们的压测任务并没有进行,而是启动了一个web服务,我们来打开http://localhost:8089 这个页面看看。

Locust+boomer压测工具介绍

  最后一个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/

Locust+boomer压测工具介绍

  找到对应操作系统的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体验了~

  我们来看看加入集群后的效果:

Locust+boomer压测工具介绍

Locust+boomer压测工具介绍

  开启压测,可以看到boomer程序显示当前开启的客户端数量了,我一共开启了20个用户:

Locust+boomer压测工具介绍

  这里的workers显示为1,其实我们还可以多搞几个worker,只需要都加入到集群就可以了。由于篇幅问题,这里就不展示了,道理都是一样的。