写点什么

发布一个轻量级的 Elasticsearch 压测工具 - Loadgen

作者:极限实验室
  • 2022 年 6 月 02 日
  • 本文字数:4470 字

    阅读完需:约 15 分钟

你是否遇到过新搭建一个 Elasticsearch 集群,但是却无法评估该集群的最大吞吐是多少,或者使用一些压测工具,比如 esrally,需要花费很大力气准备,但是却无法压测到极限速度,服务器资源跑不满,或者测试产生的数据和实际的业务有很多出入,又或者测试的请求太简单,比如查询,就是对单个固定的搜索请求进行查询,不仅测不准还可能浪费时间没有参考意义,so,有没有一个简单的工具可以支持灵活的自定义压测,并且足够快,答案是 Loadgen。

Loadgen

Elasticsearch 压测工具 Loadgen ,由极限实验室出品,基于 Elasticsearch 的开发运维需求而开发,久经实际客户环境的真实考验,简单好用速度快。


一个没有经过压测的 Elasticsearch 不是一个完整的 Elasticsearch。


Loadgen 具有以下主要特点:


  • 性能强劲

  • 轻量级无依赖

  • 支持模板化参数随机

  • 支持高并发

  • 支持压测端均衡流量控制


只有模拟自己真实业务数据场景的压测才有意义,通过使用 Loadgen 定义写入文档或者查询模板,同时将里面的变量词典化,确保每次请求都是足够随机,变量可以灵活复用,支持多个请求混合压测,最大程度模拟真实环境。


Loadgen

Loadgen 使用非常简单,下载解压之后会得到两个文件,一个可执行程序和一个配置文件 loadgen.yml,配置文件样例如下:


variables:  - name: ip    type: file    path: test/ip.txt  - name: user    type: file    path: test/user.txt  - name: id    type: sequence  - name: uuid    type: uuid  - name: now_local    type: now_local  - name: now_utc    type: now_utc  - name: now_unix    type: now_unixrequests:  - request:      method: GET      basic_auth:        username: elastic        password: pass      url: http://localhost:8000/medcl/_search      body: '{  "query": {"match": {    "name": "$[[user]]"  }}}'
复制代码

变量的使用

上面的配置中,variables 用来定义变量参数,根据 name 来设置变量标识,在构造请求的使用 $[[变量名]] 即可访问该变量的值,变量目前支持的类型有:



file 类型变量参数加载自外部文本文件,每行一个变量参数,访问该变量时每次随机取其中一个,变量里面的定义格式举例如下:


➜  loadgen git:(master) ✗ cat test/user.txt medclelastic
复制代码

请求的定义

配置节点 requests 用来设置 Loadgen 将依次执行的请求,支持固定参数的请求,也可支持模板变量参数化构造请求,以下是一个普通的查询请求:


requests:  - request:      method: GET      basic_auth:        username: elastic        password: pass      url: http://localhost:8000/medcl/_search?q=name:$[[user]]
复制代码


上面的查询对 medcl 索引进行了查询,并对 name 字段执行一个查询,每次请求的值来自随机变量 user

命令行参数

Loadgen 会循环执行配置文件里面定义的请求,默认 Loadgen 只会运行 5s 就自动退出了,如果希望延长运行时间或者加大并发可以通过启动的时候设置参数来控制,通过查看帮助命令如下:


➜  loadgen git:(master) ✗ ./bin/loadgen --helpUsage of ./bin/loadgen:  -c int      Number of concurrent threads (default 1)  -compress      Compress requests with gzip  -config string      the location of config file, default: loadgen.yml (default "loadgen.yml")  -d int      Duration of tests in seconds (default 5)  -debug      run in debug mode, loadgen will quit with panic error  -l int      Limit total requests (default -1)  -log string      the log level,options:trace,debug,info,warn,error (default "info")  -r int      Max requests per second (fixed QPS) (default -1)  -v  version
复制代码

执行压测

执行 Loadgen 程序即可执行压测,如下:


➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -compress   __   ___  _      ___  ___   __    __  / /  /___\/_\    /   \/ _ \ /__\/\ \ \ / /  //  ///_\\  / /\ / /_\//_\ /  \/ // /__/ \_//  _  \/ /_// /_\\//__/ /\  /\____|___/\_/ \_/___,'\____/\__/\_\ \/
[LOADGEN] A http load generator and testing suit.[LOADGEN] 1.0.0_SNAPSHOT, 83f2cb9, Sun Jul 4 13:52:42 2021 +0800, medcl, support single item in dict files[07-19 16:15:00] [INF] [instance.go:24] workspace: data/loadgen/nodes/0[07-19 16:15:00] [INF] [loader.go:312] warmup started[07-19 16:15:00] [INF] [app.go:306] loadgen now started.[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search[07-19 16:15:00] [INF] [loader.go:317] status: 200,<nil>,{"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search?q=name:medcl[07-19 16:15:00] [INF] [loader.go:317] status: 200,<nil>,{"took":1,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}[07-19 16:15:01] [INF] [loader.go:316] [POST] http://localhost:8000/_bulk[07-19 16:15:01] [INF] [loader.go:317] status: 200,<nil>,{"took":120,"errors":false,"items":[{"index":{"_index":"medcl-y4","_type":"doc","_id":"c3qj9123r0okahraiej0","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":5735852,"_primary_term":3,"status":201}}]}[07-19 16:15:01] [INF] [loader.go:325] warmup finished
5253 requests in 32.756483336s, 524.61KB sent, 2.49MB received
[Loadgen Client Metrics]Requests/sec: 175.10Request Traffic/sec: 17.49KBTotal Transfer/sec: 102.34KBAvg Req Time: 5.711022msFastest Request: 440.448µsSlowest Request: 3.624302658sNumber of Errors: 0Number of Invalid: 0Status 200: 5253
[Estimated Server Metrics]Requests/sec: 160.37Transfer/sec: 93.73KBAvg Req Time: 623.576686ms
复制代码


Loadgen 在正式压测之前会将所有的请求执行一次来进行预热,如果出现错误会提示是否继续,预热的请求结果也会输出到终端,执行完成之后会输出执行的摘要信息。


因为 Loadgen 最后的结果是所有请求全部执行完成之后的累计统计,可能存在不准的问题,建议通过打开 Kibana 或者 INFINI Console 的监控仪表板来实时查看 Elasticsearch 的各项运行指标。

模拟批量写入

使用 Loadgen 来模拟 bulk 批量写入也非常简单,在请求体里面配置一条索引操作,然后使用 body_repeat_times 参数来随机参数化复制若干条请求即可完成一批请求的准备,如下:


  - request:      method: POST      basic_auth:        username: test        password: testtest      url: http://localhost:8000/_bulk      body_repeat_times: 1000      body: "{ \"index\" : { \"_index\" : \"medcl-y4\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" } }\n{ \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n"
复制代码

限制客户端压力

使用 Loadgen 并设置命令行参数 -r 可以限制客户端发送的每秒请求数,从而评估固定压力下 Elasticsearch 的响应时间和负载情况,如下:


➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -r 100
复制代码


注意,在大量并发下,此客户端吞吐限制可能不完全准确。

限制请求的总条数

通过设置参数 -l 可以控制客户端发送的请求总数,从而制造固定的文档,修改配置如下:


requests:  - request:      method: POST      basic_auth:        username: test        password: testtest      url: http://localhost:8000/medcl-test/doc2/_bulk      body_repeat_times: 1      body: "{ \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[uuid]]\" } }\n{ \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\" }\n"
复制代码


每次请求只有一个文档,然后执行 loadgen


./bin/loadgen -config loadgen-gw.yml -d 600 -c 100 -l 50000
复制代码


执行完成之后,Elasticsearch 的索引 medcl-test 将增加 50000 条记录。

使用自增 ID 来确保文档的顺序性

如果希望生成的文档编号自增有规律,方便进行对比,可以使用 sequence 类型的自增 ID 来作为主键,内容也不要用随机数,如下:


requests:  - request:      method: POST      basic_auth:        username: test        password: testtest      url: http://localhost:8000/medcl-test/doc2/_bulk      body_repeat_times: 1      body: "{ \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[id]]\" } }\n{ \"id\" : \"$[[id]]\" }\n"
复制代码

上下文复用变量

在一个请求中,我们可能希望有相同的参数出现,比如 routing 参数用来控制分片的路由,同时我们又希望该参数也保存在文档的 JSON 里面,可以使用 runtime_variables 来设置请求级别的变量,或者 runtime_body_line_variables 定义请求体级别的变量,如果请求体复制 N 份,每份的参数是不同的,举例如下:


variables:  - name: id    type: sequence  - name: uuid    type: uuid  - name: now_local    type: now_local  - name: now_utc    type: now_utc  - name: now_unix    type: now_unix  - name: suffix    type: range    from: 10    to: 15requests:  - request:      method: POST      runtime_variables:        batch_no: id      runtime_body_line_variables:        routing_no: uuid      basic_auth:        username: ingest        password: password      #url: http://localhost:8000/_search?q=$[[id]]      url: http://192.168.3.188:9206/_bulk      body_repeat_times: 10      body: "{ \"create\" : { \"_index\" : \"test-$[[suffix]]\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" , \"routing\" : \"$[[routing_no]]\" } }\n{ \"id\" : \"$[[uuid]]\",\"routing_no\" : \"$[[routing_no]]\",\"batch_number\" : \"$[[batch_no]]\", \"random_no\" : \"$[[suffix]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n"
复制代码


我们定义了 batch_no 变量来代表一批文档里面的相同批次号,同时又定义了 routing_no 变量来代表每个文档级别的 routing 值。


最后,欢迎大家反馈使用过程遇到的任何问题。

用户头像

简单、易用、极致、创新 2021.11.22 加入

极限科技数据实验室

评论

发布
暂无评论
发布一个轻量级的 Elasticsearch 压测工具 - Loadgen_elasticsearch_极限实验室_InfoQ写作社区