|
|
楼主 |
发表于 2020-11-11 17:00:15
|
显示全部楼层
一、简介
9 Y% Q! b7 U! h* l6 O/ `" ^% F4 p: `* E
wrk 是一款针对 Http 协议的基准测试工具,它能够在单机多核 CPU 的条件下,使用系统自带的高性能 I/O 机制,如 epoll,kqueue 等,通过多线程和事件模式,对目标机器产生大量的负载。5 o! g9 S& x; `5 u% m5 U# ?# x1 G
/ u, |0 U0 k: m/ a! H$ {1 vwrk是开源的, 代码在 github 上:https://github.com/wg/wrk
- ~8 ]6 ?, Q9 w
: g9 V1 v; d8 |9 d3 I% F安装:https://www.cnblogs.com/savorboard/p/wrk.html1 P0 @7 T$ o ?! I$ `
1 a% N% u" b( _
% U1 j, r2 Y1 a& |/ ]4 W! f* Q
) f; I, F6 ~2 W: l2 L优势:
9 @9 Y4 n& ]3 i" n- T2 e. t6 I, S
5 b' A0 d' b7 F E, w2 M轻量级性能测试工具 a3 w |9 A4 Q* T* v2 Q
安装简单
/ T7 L! X0 i q% m学习曲线基本为0,几分钟就学会使用了
8 W0 B- S. s, C& B8 H基于系统自带的高性能I/O机制,如epoll,kqueue,利用异步的事件驱动框架,通过很少的线程就可以压出很大的并发量,例如几万、几十万,这是很多性能测试工具无法做到的。8 E) [. u1 \8 x3 P7 e) l
劣势:6 A$ y, b2 l( w
2 G% f" J/ W- E# }wrk 目前仅支持单机压测,后续也不太可能支持多机器对目标机压测,因为它本身的定位,并不是用来取代 JMeter, LoadRunner 等专业的测试工具。2 C% D, `7 p2 b" h# J
d2 ?( C$ t2 D+ X- o7 e2 Z( B
) w( I' [8 F4 {2 e+ e
二、格式及用法
4 d- W5 @2 ^ L; M; P, V8 R6 E; N5 E4 v) Z6 E8 R* t' P# D
复制代码
* e4 U% x" q, C5 N3 p8 V7 h1 qUsage: wrk <options> <url> * ^) S! [* O7 f( C( c
: n+ V/ }$ J: T, o& E$ x Options: ) s; V! Y. ]5 o/ _. n% {
-c, --connections <N> Connections to keep open
; Y% s2 M! v) n6 s2 Y -d, --duration <T> Duration of test
2 R8 `, C, o) m, D$ \ -t, --threads <N> Number of threads to use 5 V5 S8 W/ U3 n5 X: a! E0 [. l
6 N0 ^7 Z# U/ t' S -s, --script <S> Load Lua script file
' y- a8 |1 R9 x -H, --header <H> Add header to request
) Y) }8 K3 a/ N- y% X --latency Print latency statistics
+ t0 p- Q V2 k% [3 A --timeout <T> Socket/request timeout
/ o+ _" M& ?4 l; |7 y* h; O -v, --version Print version details $ s9 m9 f# {% p! o1 E/ ?# h- L
$ ~. U& T; ]7 y Numeric arguments may include a SI unit (1k, 1M, 1G)4 |/ h: P- [" h$ s3 C0 R6 {; {
Time arguments may include a time unit (2s, 2m, 2h)
; s7 p, F9 c% M$ P1 a& n复制代码& Z p% Y3 i7 ^, e) N3 E
- D1 X* A" q/ z$ W) U* J" G
' m5 {6 z, u% c; F& @$ \翻译成中文:- V7 p1 a2 y, a4 h" O- z
/ f8 w9 ?5 L J& L复制代码
O% c: _% }) Z( {: p. {使用方法: wrk <选项> <被测HTTP服务的URL> + }% X+ B) c5 |( P
( \" \3 ?( ]1 z a C J3 ^ Options: 1 L- {$ M. a W2 I/ Z/ v: B# e [
-c, --connections <N> 跟服务器建立并保持的TCP连接数量 # L( K/ x1 J0 |/ C; ?7 z" e; @
-d, --duration <T> 压测时间
$ n3 U4 Q2 R$ v- L -t, --threads <N> 使用多少个线程进行压测,压测时,是有一个主线程来控制我们设置的n个子线程间调度 ( c8 P! e8 h4 D) H
( x {+ p6 E- _* d+ \# I9 Q1 ~ L
-s, --script <S> 指定Lua脚本路径
, W8 G6 V) V6 V7 y/ F+ x% v3 X -H, --header <H> 为每一个HTTP请求添加HTTP头 ( g4 U2 O# \' q# o( o$ C
--latency 在压测结束后,打印延迟统计信息
7 s( m) A3 C4 @7 e! e" n5 ]: c) @ --timeout <T> 超时时间
; W; j! @3 V' q$ V- Q -v, --version 打印正在使用的wrk的详细版本信 9 Q- f8 ?, z+ ~
/ q, a9 N% z+ ^6 B" k
<N>代表数字参数,支持国际单位 (1k, 1M, 1G)
. U- ^/ ]% E q3 F0 t* d8 o <T>代表时间参数,支持时间单位 (2s, 2m, 2h)
" S, g. I1 i* q+ T+ |1 A4 I复制代码
) ~' S! @4 |" e - y* i+ }! c) s8 p
, E3 _; t0 S4 V1 w
2 C: @% r% r z* e6 `* y5 R( g3 \- u7 D; a0 p+ Y$ D/ y
三、简单压测及结果分析! d0 |% y; K; T5 j% @$ m
; j1 L; a, ^" k; @* o
做一个简单的压测,分析下结果:" p* R( {: ?% @( _1 n. _6 M
^" \/ b" Z' \& O: k5 |wrk -t8 -c200 -d30s --latency http://www.bing.com
, x8 G. s s1 i* \0 H * S5 b* ^9 k/ y& W) d, p" k
6 G" N" m# r; Z0 l: E* f- D, |
输出:
" F( }' U( K4 j) {1 t% }3 g4 f7 W# j5 b& n2 M$ ^
复制代码
* O7 |$ W* C \5 wRunning 30s test @ http://www.bing.com
0 L# s, U* J0 V& f o% \4 l! q- J: h
8 threads and 200 connections
- }, f6 e9 R- Q) A- l$ ]
$ E6 t8 @3 G. g; G8 |! s' W Thread Stats Avg Stdev Max +/- Stdev# r' O& p G% ?
Latency 46.67ms 215.38ms 1.67s 95.59%
2 d2 f+ `2 ^2 }! T5 J Req/Sec 7.91k 1.15k 10.26k 70.77%! G# `3 g$ P H$ a3 R( ~; d% T# ?
# \' N/ W5 r0 x" J Latency Distribution
1 O& R) G. K# T, c' ]4 b4 p 50% 2.93ms; D) o! U' a' \; X3 C
75% 3.78ms
$ g! `7 t2 g9 Z- i: R 90% 4.73ms
2 ]4 @# L9 f. I: r: |2 {* c" w4 a3 f, K% r 99% 1.35s
3 R% o" ^! m7 H9 a 1790465 requests in 30.01s, 684.08MB read& H# s: l+ ]. A
Requests/sec: 59658.29 {% y$ h1 o3 f2 |2 l" e z
Transfer/sec: 22.79MB
1 k& P; _" t" t; z9 @3 H2 u0 s复制代码
' }* H. ]8 |1 o& I以上是使用8个线程200个连接,对bing首页进行了30秒的压测,并要求在压测结果中输出响应延迟信息。
! q( J5 t* z! Z- J% U, n; C0 \" A9 S$ J" X: g
以下是解释压测结果: C$ ?! g# l: f) N( s8 J8 ?& x
# F# ~4 {. z% W' H* o5 v! W
复制代码
. u, |- ^1 r3 G" n2 lRunning 30s test @ http://www.bing.com (压测时间30s)
9 K l. t8 k+ z/ T" ]+ E9 P6 o! u" W/ D4 k9 p4 @9 ?
8 threads and 200 connections (共8个测试线程,200个连接)
* J% O j% y$ z! [- E" W4 o L1 b7 ^9 J6 Q3 F8 T! e
Thread Stats Avg Stdev Max +/- Stdev# `! [# O' d5 C
(平均值) (标准差)(最大值)(正负一个标准差所占比例). U& S1 I a# |; T# q9 N
Latency 46.67ms 215.38ms 1.67s 95.59%) E4 t7 S* ^) @( E1 r' l& k$ m0 `
(延迟)$ w+ k2 E6 D" S
Req/Sec 7.91k 1.15k 10.26k 70.77%
# N: } C& h! M: m1 ^ (处理中的请求数)5 _% x& r2 c7 |% @- v# V, p. z
- G5 [' B; I* f$ ]& c% J% M$ X Latency Distribution (延迟分布)& _) |" |1 S' x0 @0 k
50% 2.93ms
7 p) b2 X3 g9 k7 r) w) D$ Z. N 75% 3.78ms: @4 z* ~" q" I, W- f7 G; d
90% 4.73ms( X0 I P, J! i
99% 1.35s (99分位的延迟:%99的请求在1.35s以内)' y7 Z- [2 @/ U7 Z9 b
1790465 requests in 30.01s, 684.08MB read (30.01秒内共处理完成了1790465个请求,读取了684.08MB数据)! r6 G. M1 ~# s+ o8 z
Requests/sec: 59658.29 (平均每秒处理完成59658.29个请求)$ z" W/ e: }5 B8 t: H2 ^/ v! f
Transfer/sec: 22.79MB (平均每秒读取数据22.79MB)$ x5 d) d$ P1 m( W, {
复制代码# F7 @; K7 e4 `' J. [2 H% X u
# P* x" f- z) c9 r
' v: x- J! r' \ C/ T. u/ g
; F2 T2 c# C& i( Z$ t- F- d
2 |& P- r. e& |0 G# p9 l1 U四、使用lua脚本进行压测
+ j; I3 N# W* Z+ L& u! k6 ~% J2 D6 ]% M# n) C# s
lua脚本是一种轻量小巧的脚本语言,用标准c语言编写,并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为程序提供灵活的扩展和定制功能。wrk工具嵌入了lua脚本语言,因此,在自定义压测场景时,可在wrk目录下使用lua定制压测场景。3 {. i }* @& I0 d& E
0 ~7 I1 r& m. a3 y9 Q 1、lua声明周期* A1 L6 k, y# ~2 o! q
" h, @: p' j- m9 v; X2 `
共有三个阶段,启动阶段,运行阶段,结束阶段。wrk支持在这三个阶段对压测进行个性化。
( _$ f2 R- ~- I: ?% B( s" b! [- M( J5 L* t0 _# s
启动阶段
% z0 ?3 \$ N- s- kfunction setup(thread)
8 ]- W) p' y$ A0 u" u
( z \8 Q- A7 Z+ q: k
1 ?9 l9 b7 w) \4 G/ X K在脚本文件中实现setup方法,wrk就会在测试线程已经初始化但还没有启动的时候调用该方法。wrk会为每一个测试线程调用一次setup方法,并传入代表测试线程的对象thread作为参数。setup方法中可操作该thread对象,获取信息、存储信息、甚至关闭该线程。
- t$ h+ u( M9 F- [' ~$ h( [' F) x- W& G" W" u$ ?
thread.addr - get or set the thread's server address" D K$ o. u/ [3 ^4 J
thread:get(name) - get the value of a global in the thread's env
% g4 e! Z; S" z3 _- N7 J; f" M7 fthread:set(name, value) - set the value of a global in the thread's env9 z9 K ?3 c; M
thread:stop() - stop the thread) Y/ o& g& }& j- x u* W& F
& T q- K* P( `8 ^0 s+ b
0 x1 r" s# Q; _8 _3 D1 @运行阶段! L9 a9 ?3 C) {2 a4 [1 H
function init(args) --由测试线程调用,只会在进入运行阶段时,调用一次。支持从启动wrk的命令中,获取命令行参数;
! A# R8 M' o% n' w7 z+ A9 b6 ~8 Sfunction delay() --在每次发送request之前调用,如果需要delay,那么delay相应时间;
; ~ M! |6 j* S9 c+ Ifunction request() --用来生成请求;每一次请求都会调用该方法,所以注意不要在该方法中做耗时的操作;3 |; u2 h Q U% @3 F5 m
function response(status, headers, body) --在每次收到一个响应时调用;为提升性能,如果没有定义该方法,那么wrk不会解析headers和body;: ?- p1 g0 [/ s! f' P2 A! m( L
& \. s! H2 H2 d" R1 I& y
: _9 E# M) I' H( d' m2 S8 A
结束阶段
. t! F) v4 q5 w8 Dfunction done(summary, latency, requests) --在整个测试过程中只会调用一次,可从参数给定的对象中,获取压测结果,生成定制化的测试报告。
5 H& h5 a5 [) p4 J# L+ e; U
" ~2 o2 P) C! Y4 A' |% [. N( ]5 e H2 _# n
2、自定义脚本中可访问的变量和方法:
7 ?+ O" m; o! w$ c4 e' g" K. c3 y' _
变量:wrk
, m# \; o- `/ `" J7 T, y \# z3 h/ V. C" P. P" V! U
复制代码
) {5 x3 ?! s5 Jwrk = {: a# p9 D& y' l' |
scheme = "http",
2 ~0 ~' j. Q% o: J5 ^# ~0 E3 |+ | host = "localhost",7 Q7 t4 X$ E' c
port = nil,
, E; o! P6 X& m& Z* p method = "GET",
4 f! Y3 n7 F8 f/ P/ h( Y3 e' \" v path = "/",
" e" y: R' D6 W/ Z" r headers = {},
6 A \0 |. [. ?$ B) @& D% M body = nil,
: I3 X3 b) @/ S thread = <userdata>,
6 d( m3 J- s5 r0 m' }0 P' k }: q$ V% Z9 u" V3 G1 G) O
复制代码4 z. X9 z1 w& A; w0 o) b ]5 Y. g
! y" V& m3 ]: u; v
( k4 X- [: g( o0 K/ v6 Y2 V 方法:wrk.fomat wrk.lookup wrk.connect
+ Y6 `! v8 \0 e% w- }* c3 i
% Z% c' @. ^# ?function wrk.format(method, path, headers, body) --根据参数和全局变量wrk,生成一个HTTP rquest string。
5 ^3 [/ y! [6 z$ ]$ }1 @function wrk.lookup(host, service) --给定host和service(port/well known service name),返回所有可用的服务器地址信息。 R; s3 c* J/ @, x1 g
function wrk.connect(addr) --测试与给定的服务器地址信息是否可以成功创建连接7 j& h0 ^* |% i' H
* s* ^ F! Z Z, X" w" ~: k+ x9 B/ p Q0 X0 I
3、lua脚本压测实例
' y4 P# z4 F- k! r
6 |" i9 |2 o# S1 s( ?2 @' S 压测命令:wrk -t8 -c200 -d30s --latency -s test.lua http://www.bing.com
7 Q+ J6 w1 [) ~9 K6 _
" c: X+ N: w1 b7 F7 C& b3 t test.lua是用lua写的压测脚本,如下是压测脚本的实例:
0 Y: s7 u2 ?0 n f
# _* S& M5 J Z( I) Y$ N: ~ 使用post方法压测6 g9 Q' b* j8 S% C" o0 C' X1 h* l# k1 f
. R1 d) d5 q) G' ^
复制代码1 c) E" R; a) k. Z: }$ R
wrk.method = "POST"
. T/ \4 c6 x/ r) vwrk.headers["S-COOKIE2"]="a=2&b=Input&c=10.0&d=20191114***"
( P' d* C0 o9 jwrk.body = "recent_seven=20191127_32;20191128_111"2 s8 m' k) m; M( N$ T3 Z
wrk.headers["Host"]="api.shouji.**.com": R3 r. @& N6 f$ U# g8 q3 b
3 W; p) K/ D9 q. V1 `; kfunction response(status,headers,body)
/ z3 _9 S s# h( I2 | if status ~= 200 then --将服务器返回状态码不是200的请求结果打印出来% n( C9 V u" K7 {1 U
print(body)
* ]; g. Q. E/ S1 ^) `% [. M: X$ } -- wrk.thread:stop(): [9 a3 h& Y) M7 T
end
0 A5 O( o) w) J/ U3 vend, P* C" D+ a) w5 \
复制代码
; r/ s* t9 x w* n# t1 \2 G, x& S 1 B! r& t1 ?3 d! K' S; J8 O
7 d' O; c3 [$ w' Z+ S* o1 W* c
发送json
4 U( S" f! O K
+ j$ q) F# E. Y0 H6 g& Z: [' \复制代码
2 l2 N: R- h4 ?6 Frequest = function()
3 ~( Q" \1 O$ x local headers = { }& C; ?0 {* U* U8 P" t
headers['Content-Type'] = "application/json"
2 t/ L+ J- q* s, f5 W body = {1 S6 T( o( }: N5 I% q. J5 k
mobile={"1533899828"},* ?. {8 q" H1 Z" n3 k% d
params={code=math.random(1000,9999)}
b" C8 h3 s, G/ X0 t ~0 @ }
3 E* i7 \4 d! ] local cjson = require("cjson")$ ^0 i8 i: i' t, t8 [: f
body_str = cjson.encode(body)
( N- c; _& _; z$ B- l return wrk.format('POST', nil, headers, body_str)
. A- O; r$ W Q4 I, \end
) r# u' K6 |3 ^3 H$ `1 d- u: i复制代码
4 C2 r. i6 c7 S
7 F4 I0 x, {! ~, w! b' }1 C1 Z2 u, C& C& V ^- G
wrk读取文件,实现随机header-cookie$ |% B4 z9 _( a: `6 @9 }
4 v( r2 ]: _9 }0 x
复制代码! p$ Q- E& ~+ T5 c8 c- t X1 | U: d7 }
idArr = {}) c+ I& v9 g3 I _; y( g
falg = 0
# | A X. g9 q1 M- i: M' Ywrk.method = "POST"
4 l8 \4 i' F" ]8 X8 Awrk.body = "a=1"
; D8 N0 O3 g( v6 A' {3 Ifunction init(args)( Y' R$ Z+ g2 X* m
for line in io.lines("integral/cookies.txt") do
5 @$ K* w; l! Q* \! i print(line)
9 J+ r- }& k4 _& G+ s+ o0 a idArr[falg] = line
4 H& @2 Z+ C: _: F! s: F. k2 J* C( Q falg = falg+1
+ L- q7 H7 Z/ u+ e end/ |: z" k# x5 m [% E: m3 {/ D) [, }
falg = 0$ c. D+ U6 v, \' W+ J% t) O
end
; `' t" n2 b r# H: [( E$ Z! }" V, \' A
--wrk.method = "POST"
' M5 o% F* v, u" h: O0 e+ \- T--wrk.body = "a=1"
: V- f* L- ]: C2 g) w7 w--wrk.path = "/v1/points/reading"6 b# \% J$ h; f4 d' }- H0 Y
2 X: A, W& G; `/ X! B3 T7 A
request = function()
2 d( _2 Z6 W" V: h- F5 n2 a/ i parms = idArr[math.random(0,4)] --随机传递文件中的参数
V3 ]+ V/ a9 y( v* H9 E, r --parms = idArr[falg%(table.getn(idArr)+1)] 循环传递文件中的参数9 m6 Z1 ^5 H: f% ~
wrk.headers["S-COOKIE2"] = parms; `/ B t0 M8 ^2 a ? _9 d
falg = falg+16 F9 n7 H3 r I U+ S) X9 }
return wrk.format()
6 P2 e0 w) D% ]+ kend* e5 I) v6 G5 Q
复制代码
7 e2 `5 ~( X+ [ r' ]: z - w" o' H: v7 k7 A5 u- f2 ~
9 i$ v) d7 ?) z2 Y. y! S; N4 [" S wrk创建数组并初始化,拼接随机参数# H# w# k) a6 ~) G. m) q: x
$ ?8 j# t* S1 A G复制代码2 {$ Z6 J( c( t" s; f
idArr = {};
6 L" e7 f+ [, G( }* Kfunction init(args). q' j2 c( d6 e0 F' U8 j2 y
idArr[1] = "1";7 Z% u3 V/ H+ I8 Q, R8 H/ J1 ~
idArr[2] = "2";4 U. ^/ J& r+ g e I/ a
idArr[3] = "3";
8 N" {/ W, r, F3 {2 y! l$ ] idArr[4] = "4";
: [" @. I0 B8 p3 W8 y! wend" k0 L8 [9 X( o& d- P1 q6 L) I- @
. F- |# {. L. V( z# r, ]% T% x
request = function()
& Q7 ^) E3 U. X' a4 [% t parms = idArr[math.random(1,4)], m! G/ k; g0 @! x
path = "/v1/points/reading?id="..parms
) c, h8 D' R4 z% Z7 s b return wrk.format("GET",path)
: G2 M2 [. m- d. lend
5 N$ _2 x' j( d1 Y复制代码 |
|