这两天抽空使用了一下两款压测工具node
而且经过两款工具对产品的两个环境进行了测试ios
wrk自身性能就很是惊人,使用epoll这种多路复用技术,因此能够用少许的线程来跟被测服务建立大量链接,进行压测,同时不占用过多的CPU和内存。git
命令很是简单github
wrk -t8 -c200 -d30s --latency "http://www.baidu.com"
这样就能够进行最简单的压测。可是真实使用起来确定会有复杂的场景,好比先要登陆取到token再进行下一步。好在wrk支持lua脚本,提供了几个阶段的hook来让用户自定义逻辑,具体能够看github上的官方提供的script sample。docker
我这里举一个获取token的例子json
-- @Author: wangding -- @Date: 2017-12-06 15:13:19 -- @Last Modified by: wangding -- @Last Modified time: 2017-12-06 23:57:49 local cjson = require "cjson" local cjson2 = cjson.new() local cjson_safe = require "cjson.safe" token = nil path = "/api/auth/login" method = "POST" wrk.headers["Content-Type"] = "application/json" request = function() return wrk.format(method, path, nil, '{"username":"demo@demo.com","password":"demo"}') end response = function(status, headers, body) if not token and status == 200 then value = cjson.decode(body) token = value["token"] method = "GET" path = "/api/contact?size=20&page=0" wrk.headers["Authorization"] = token end end
request
和 response
分别是两个hook,每次请求都会调用,那么这里request的逻辑就是一开始就使用POST
请求/api/auth/login
而且带有body,请求完成进入response,第一次token确定是nil,因此把repose的token解析出来付给全局变量token
,以后改写全局变量为GET
请求地址/api/contact
而且设置了header包含Authorization
。api
这样实际是变通的实现了一个简单scenario的测试,那么问题来了,若是场景更复杂怎么办?写确定是能够写的,可是并不直观,因此wrk不太适合一个包含有序场景的压力测试。tomcat
再来看一下wrk的report,这一点是我最喜欢的bash
wrk -t8 -c200 -d30s -H "Authorization: token" --latency "http://10.0.20.2:8080/api/contact?size=20&page=0" Running 30s test @ http://10.0.20.2:8080/api/contact?size=20&page=0 8 threads and 200 connections Thread Stats Avg Stdev Max +/- Stdev Latency 769.49ms 324.43ms 1.99s 72.08% Req/Sec 33.37 21.58 131.00 62.31% Latency Distribution 50% 728.97ms 75% 958.69ms 90% 1.21s 99% 1.74s 7606 requests in 30.03s, 176.69MB read Socket errors: connect 0, read 0, write 0, timeout 38 Requests/sec: 253.31 Transfer/sec: 5.88MB
开启8线程,每一个线程200个链接,持续30s的调用,能够看到报告中直接给出了最关键的指标QPS,这里的值是253.31。平均响应时间是33.37ms。简单直接,很是易懂。并发
可是这里面有个坑就是cjson这个lua module的使用,不可使用lua5.2,必须使用lua5.1并且须要特定的wrk和cjson。我直接使用docker来封装这个运行环境,坏处是docker使用host模式自己性能可能就有影响。
一开始看到Artillery主要是由于它支持带场景的测试,也就是带有步骤,看一眼获取token再进行下一步的脚本。
config: target: "http://10.0.20.2:8080" phases: - duration: 30 arrivalRate: 100 scenarios: - flow: - post: url: "/api/auth/login" json: username: "demo@demo.com" password: "demo" capture: json: "$.token" as: "token" - log: "Login token: {{ token }}" - get: url: "/api/contact?size=20&page=0" headers: Authorization: "{{ token }}"
flow
就是表示步骤,duration
表示持续30s,跟wrk不一样的是没有thread的概念,Artillery是nodejs写的,arrivalRate
表示每秒模拟100个请求,因此两个参数乘起来就是3000个请求。看一下报告什么样:
All virtual users finished Summary report @ 12:45:41(+0800) 2017-12-08 Scenarios launched: 3000 Scenarios completed: 3000 Requests completed: 3000 RPS sent: 98.33 Request latency: min: 15.7 max: 179.1 median: 19 p95: 25.8 p99: 37.5 Scenario duration: min: 16.4 max: 191.4 median: 19.8 p95: 27 p99: 44.6 Scenario counts: 0: 3000 (100%) Codes: 200: 3000
这里的RPS sent
是指前10s平均发送请求数,因此这个和咱们常说的QPS仍是不同的。若是想提升request的总数就要增长arrivalRate
,好比上文wrk一共发了7606请求,那么这里arrivalRate
提升到200一共能够在30s发6000次,可是改完就悲剧了,
Warning: High CPU usage warning. See https://artillery.io/docs/faq/#high-cpu-warnings for details.
Artillery一直在不断的告警,说明这个工具自身的局限性致使想要并发发送大量请求的时候,本身就很占CPU。
wrk小巧并且性能很是好,报告直观。可是对于带多个步骤的压测场景无力。
Artillery太耗资源,并且报告不直观。不建议采用。
除此以外惟一带场景的测试工具就是Jmeter了,可是Jmeter自己使用JVM是否能够短期模拟大量并发,仍是须要测试,建议与wrk作对比实验。
在用wrk测试GET请求的时候,发现不管如何提升链接数,QPS都是在250左右,此时CPU和内存都没有占满。怀疑是有其余瓶颈。最后发现Spring Boot内嵌的tomcat线程没法突破200,因此看了一下文档,发现默认最大线程数就是200,对application.yml
进行了调整(同时调整了多个服务,包括gateway)
server: tomcat: max-threads: 1000 max-connections: 2000
调整以后开启8线程,每一个100个链接测试
Running 30s test @ http://10.0.10.4:8769/api/contact?size=20&page=0 8 threads and 100 connections Thread Stats Avg Stdev Max +/- Stdev Latency 235.56ms 267.57ms 1.98s 91.07% Req/Sec 72.12 30.19 190.00 68.17% Latency Distribution 50% 166.46ms 75% 281.10ms 90% 472.03ms 99% 1.45s 15714 requests in 30.03s, 4.77MB read Requests/sec: 523.29 Transfer/sec: 162.56KB
能够看到QPS达到了500以上直接翻倍了,再尝试提升链接数发现瓶颈就在内存了。
此外以前用公网作了一次压测,QPS只有10左右,看了一下阿里云的监控原来是出口带宽形成的,只有1MB的出口带宽,链接数调多大也没用。
将来还须要进行场景的细化,再决定是否使用不一样的工具进行测试。