|
|
楼主 |
发表于 2020-11-11 17:00:15
|
显示全部楼层
一、简介: H2 n0 ^0 {* U0 d5 C$ R3 i
& w) e& Y- B8 Y1 M2 @$ k
wrk 是一款针对 Http 协议的基准测试工具,它能够在单机多核 CPU 的条件下,使用系统自带的高性能 I/O 机制,如 epoll,kqueue 等,通过多线程和事件模式,对目标机器产生大量的负载。
% n, R, c! t! B1 P8 Z' ~
4 Q, L" u: B; \8 n( a! c9 _" awrk是开源的, 代码在 github 上:https://github.com/wg/wrk& b( _4 W' }3 U6 }5 B
0 Z2 l0 A4 Y# }6 y安装:https://www.cnblogs.com/savorboard/p/wrk.html! Q4 [3 i5 R+ i: f% ?: ]! y) h
! P1 C+ b+ i: w: _0 u3 d; m
- Z f% a E8 g0 d' N- \$ p3 ^
/ h0 u8 j0 r" [9 m. F- X优势:: N8 O. q* J" Q1 I
4 w* ^3 W; q2 l5 i+ m# H轻量级性能测试工具
! D* a M4 Z* j% w安装简单
* W6 X6 @0 k) ^; |+ q9 u6 K学习曲线基本为0,几分钟就学会使用了) f2 V! X2 \' T* z. Y/ Y$ v( Z
基于系统自带的高性能I/O机制,如epoll,kqueue,利用异步的事件驱动框架,通过很少的线程就可以压出很大的并发量,例如几万、几十万,这是很多性能测试工具无法做到的。* t) t. o3 l; A, t* Q; c
劣势:
- A' z2 m* X: D% ?9 s0 @5 o: [3 X$ L8 T
& V4 [3 j8 S" {5 v% y7 K& fwrk 目前仅支持单机压测,后续也不太可能支持多机器对目标机压测,因为它本身的定位,并不是用来取代 JMeter, LoadRunner 等专业的测试工具。' k' U. N4 V, }, ?! ?9 S
* u! O! F# Q9 z" F; ?
W2 G8 [; c3 D [$ ?" k3 V
二、格式及用法& s! z( _5 x3 z% G
! n0 z% y) f& x" L% {, ~复制代码( n; n( L: m4 v7 u1 ~( s
Usage: wrk <options> <url> ' C6 E5 ~( r' `/ `: `( ?# ]% _
- a1 L! Q6 v; m7 U/ w# K Options:
n2 G* l) Q1 @& p3 } p -c, --connections <N> Connections to keep open 6 Q5 ^+ g4 ~. J9 P/ \6 Y
-d, --duration <T> Duration of test
: J) C% C/ ` z$ w3 K; F S -t, --threads <N> Number of threads to use
1 P: D7 r: X$ R, \ 6 r% ~8 \& z* _2 T- ]
-s, --script <S> Load Lua script file # f# _6 w" K8 M2 ]! y. `
-H, --header <H> Add header to request
/ d1 \* d! _- D# |8 Q+ A --latency Print latency statistics
' N0 S7 Y/ U" e# N+ D a --timeout <T> Socket/request timeout
2 z- Y0 x/ m* J- F7 W -v, --version Print version details
% [9 o% l" E: W4 A
7 a& N! P! K8 ~& N% e( \. x* e Numeric arguments may include a SI unit (1k, 1M, 1G)
5 f' Y j0 p# L- K! | E Time arguments may include a time unit (2s, 2m, 2h)
: l$ c. E2 O1 b. u: o复制代码
( E$ j" P# g7 r, l3 p* @$ ^ 3 ]" i. p9 g. r4 j! o
% k, o( F, E+ k2 v; M
翻译成中文:
! t9 t2 g/ j! x, T- S8 Z! ?# T* _9 q; x* j* n
复制代码
/ _) z3 V- H. ]) J2 l使用方法: wrk <选项> <被测HTTP服务的URL> + Q/ }9 Y1 Y7 e% t2 x3 g
/ G% T8 @4 Q, ~3 s Options: 3 o- x4 C* |* q% i, z+ O j% w
-c, --connections <N> 跟服务器建立并保持的TCP连接数量
# h( ?6 Z3 a% g8 |+ ]2 ?. F -d, --duration <T> 压测时间
& o3 a+ C0 r4 K: M' B -t, --threads <N> 使用多少个线程进行压测,压测时,是有一个主线程来控制我们设置的n个子线程间调度 2 y6 _, S4 _( M
: K* F. b, ~: L" k, m: v9 p: T
-s, --script <S> 指定Lua脚本路径 6 p: i4 B1 A0 A' c' j! Q
-H, --header <H> 为每一个HTTP请求添加HTTP头 . z+ I+ C$ g: P& v( }/ D
--latency 在压测结束后,打印延迟统计信息
# J- B( H$ @5 D --timeout <T> 超时时间 , T( o, o/ s" D" r
-v, --version 打印正在使用的wrk的详细版本信 - N" [8 `( c# w9 q9 [+ B
" J# {3 c! r1 E- M <N>代表数字参数,支持国际单位 (1k, 1M, 1G)
N0 S' r; u3 h2 F4 f <T>代表时间参数,支持时间单位 (2s, 2m, 2h), ]% e# H9 n. |2 N; d; k
复制代码
+ e! J# s! p% u 9 i. w3 R, ]' C7 H% `, M& a/ S3 u
2 w6 P& Y+ t% S1 v$ z j
8 C- p; M1 @3 U, N7 @ v
3 U2 G5 O' h) n; N9 G三、简单压测及结果分析# Z" m% }6 E1 H+ y9 \
( n8 n; G' @& [" J
做一个简单的压测,分析下结果:* l9 V' w, b' x8 ]8 _) Y4 b
7 y2 O/ x6 [/ I0 L7 F; ~. lwrk -t8 -c200 -d30s --latency http://www.bing.com% A0 V! ?9 L- v- L! B
' h8 @# n, r0 e" l8 Q, P$ G4 q# W
: @1 g+ r0 T9 J6 |" {: @! ^输出:
8 |: s/ H; A6 ?5 l0 m) y. f3 h7 }2 o2 f8 z& n
复制代码
% Q# S, W1 O) C, o0 [/ ]Running 30s test @ http://www.bing.com. i/ t- Q5 D8 U" R% s
4 C& F; A" B/ x; W
8 threads and 200 connections
+ ?& R& ^! t; {, @ I
5 n: Q7 k% h! I) m0 m Thread Stats Avg Stdev Max +/- Stdev
$ |) |4 Q6 e; S& t; r Latency 46.67ms 215.38ms 1.67s 95.59%8 L" Y# _9 N2 y9 b! N( A$ G
Req/Sec 7.91k 1.15k 10.26k 70.77%* k& e" x' C4 k0 o! k6 t2 ?( W
5 b, }; N* X& Z- Z
Latency Distribution6 S6 n& A7 y( b* j# c& E4 `
50% 2.93ms
! b' c8 c0 @1 k0 Q 75% 3.78ms
$ m8 B* ^) m/ u0 o8 F, Q 90% 4.73ms
$ U; S! @) P6 {6 r7 R8 f, a( j 99% 1.35s
1 d S, T4 k$ D. u! }; \ 1790465 requests in 30.01s, 684.08MB read
# v; u) q$ r; Q- t( L+ U1 |Requests/sec: 59658.29' u) s0 v/ {5 w! \! U' I2 v
Transfer/sec: 22.79MB2 t) R, O/ d. V6 a) _! u
复制代码
3 `: c9 v( k' I以上是使用8个线程200个连接,对bing首页进行了30秒的压测,并要求在压测结果中输出响应延迟信息。
0 T7 C$ d7 L# M' M
: @# k# }3 S9 U( k4 O以下是解释压测结果:
# ?+ L6 e0 \0 A h3 F9 V7 a# J7 s& O
复制代码
7 A; |" x0 U! TRunning 30s test @ http://www.bing.com (压测时间30s)$ m& ~7 s' t) R5 l5 \& ^. M$ r
4 b: n: H& X( [3 r( |2 B- _% Q$ G
8 threads and 200 connections (共8个测试线程,200个连接)
0 I+ v; `; S9 }3 }1 p( E0 w0 ^- v
Thread Stats Avg Stdev Max +/- Stdev% ]# [# B" {0 T5 y2 L
(平均值) (标准差)(最大值)(正负一个标准差所占比例)2 J& U8 h' U( ?5 g3 w4 p
Latency 46.67ms 215.38ms 1.67s 95.59%: v/ J. } d0 a
(延迟)
9 N% X: h$ ^: G* G' T; F4 X Req/Sec 7.91k 1.15k 10.26k 70.77%- k5 a. r r B; J3 B, D. H
(处理中的请求数)0 Y" k- ]: ?0 f. k' n0 D2 t
+ |6 x, x* A1 a; s- A
Latency Distribution (延迟分布)
; S8 u. A# E( C& g) @6 [ 50% 2.93ms
/ s8 W9 ]1 Z9 c4 R7 \$ ^ 75% 3.78ms& Y* U. ^# i1 G4 D# c+ P
90% 4.73ms
& A1 G B6 m* l2 z% Y 99% 1.35s (99分位的延迟:%99的请求在1.35s以内)+ l7 C6 Z$ I& Y- j: F8 V, O
1790465 requests in 30.01s, 684.08MB read (30.01秒内共处理完成了1790465个请求,读取了684.08MB数据)4 e3 {7 y( W( {' w$ w
Requests/sec: 59658.29 (平均每秒处理完成59658.29个请求)
3 O% l( e4 z0 E1 X+ LTransfer/sec: 22.79MB (平均每秒读取数据22.79MB)
% ]5 Z8 [9 v' h3 p/ x/ @复制代码- E+ n) z) t% B' F* S4 H
( x) ]$ r; ]8 ]. H9 i
8 u2 ^/ f& j, U( P/ W7 J/ U
; {' \8 ^0 g- m% U; m; T/ [( H+ v! C* L, ?! N( y" a: ?5 u
四、使用lua脚本进行压测
$ Z, S! J7 q) N4 T6 ^: }( E y. `( q6 Q+ O& ^! E
lua脚本是一种轻量小巧的脚本语言,用标准c语言编写,并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为程序提供灵活的扩展和定制功能。wrk工具嵌入了lua脚本语言,因此,在自定义压测场景时,可在wrk目录下使用lua定制压测场景。
& b* v3 B2 s% ~" b, b2 ]$ L0 Q; A7 X n1 T7 e& v2 v" T
1、lua声明周期
5 f. j+ ~6 T' ~- s% q: V; ~, y* T% L, T" k+ M6 C- O+ x, K4 r9 ~
共有三个阶段,启动阶段,运行阶段,结束阶段。wrk支持在这三个阶段对压测进行个性化。 " I2 `7 ?' T, @1 x, _; ~3 T: _# P
0 p/ W+ v1 X5 P3 _1 c启动阶段# z4 {7 {5 C" \, A7 ~
function setup(thread); V: {7 q8 [1 @( g
1 J; Z; H8 I. U+ P" B, \0 r
! [( T2 C/ W( ~+ {
在脚本文件中实现setup方法,wrk就会在测试线程已经初始化但还没有启动的时候调用该方法。wrk会为每一个测试线程调用一次setup方法,并传入代表测试线程的对象thread作为参数。setup方法中可操作该thread对象,获取信息、存储信息、甚至关闭该线程。6 F- E$ s( b/ o! O
9 ~% ]7 @, u% l( Z$ b8 Nthread.addr - get or set the thread's server address) F( X# k0 g7 M% V' q, B9 H
thread:get(name) - get the value of a global in the thread's env
' h h" j. }! E5 j/ F/ }$ C: jthread:set(name, value) - set the value of a global in the thread's env) R: o# n. t$ W' m0 B( C
thread:stop() - stop the thread$ i9 c! ^8 ^% \
; x% \4 Y- B1 N2 ?
/ c) T3 {* d+ L7 G# x; {8 g1 t- _3 B运行阶段9 a3 d, v' V, k2 ~3 R" ]
function init(args) --由测试线程调用,只会在进入运行阶段时,调用一次。支持从启动wrk的命令中,获取命令行参数;
) [* _& R2 ^4 i b7 hfunction delay() --在每次发送request之前调用,如果需要delay,那么delay相应时间;, H& x, [( f$ J* i9 Z$ ]
function request() --用来生成请求;每一次请求都会调用该方法,所以注意不要在该方法中做耗时的操作;: w* F, I ^6 Y* j! N
function response(status, headers, body) --在每次收到一个响应时调用;为提升性能,如果没有定义该方法,那么wrk不会解析headers和body;
1 Y/ d9 Q9 @3 }+ y! B8 B$ f4 O 4 f3 i! [3 c' v% W& i
s, `/ }8 J ^
结束阶段
) \( n& T: x+ c Y; g, {* f+ Vfunction done(summary, latency, requests) --在整个测试过程中只会调用一次,可从参数给定的对象中,获取压测结果,生成定制化的测试报告。) d0 i1 e( X; n& a: N
0 ~. V- H6 i" H2 t9 G/ R/ p
, e4 E4 _0 t2 _9 \ 2、自定义脚本中可访问的变量和方法:
8 Y/ l7 b# m1 e( l6 S
# v' ~3 q) l# T6 V0 Y 变量:wrk- m2 q6 i* E2 C: w2 [6 f
1 L1 V5 C& ~1 ^$ i; ?6 P1 Q复制代码0 G, C ]0 h% [+ c- v" b4 X
wrk = {
- k9 N' U% h3 V2 L$ L5 o9 [ scheme = "http",
6 Q* m* Y7 G' ]( Q host = "localhost",* h' N( e5 `0 P5 L
port = nil,
" U1 o9 f! E6 g method = "GET",
" {7 |' V' G' R$ ]; L. p path = "/",3 x- T# G/ a+ [% b
headers = {},
/ N" v: j% `6 a8 `6 O body = nil,% f8 ^0 z! k/ o. u( f
thread = <userdata>,
( m, @# y1 q9 |. H |4 o: D }0 J$ c" H( S( g7 D8 k: g
复制代码
2 I) H e2 s4 _+ p. A; K
$ p- t0 D, h8 w8 h
6 p3 B: L& w0 M- u( O7 r# Z 方法:wrk.fomat wrk.lookup wrk.connect
9 k' \! d' k" a* a; W
& q5 q' f7 s+ J& C0 E0 u9 Dfunction wrk.format(method, path, headers, body) --根据参数和全局变量wrk,生成一个HTTP rquest string。
6 d) l3 Z% k! W" E- W. Ufunction wrk.lookup(host, service) --给定host和service(port/well known service name),返回所有可用的服务器地址信息。9 T5 V* _7 R5 ]1 M8 p+ ?# _
function wrk.connect(addr) --测试与给定的服务器地址信息是否可以成功创建连接
" N3 J6 \9 ? e: q9 P3 P & F: p# K$ l! ?* I2 X7 m
\6 G2 n) F3 a9 M0 W$ q4 x3、lua脚本压测实例( M( l+ P1 H9 v% A4 y
9 M% ?5 `6 d) i
压测命令:wrk -t8 -c200 -d30s --latency -s test.lua http://www.bing.com2 y' e2 x1 d- ?, M# L
* P+ i. \ m( T test.lua是用lua写的压测脚本,如下是压测脚本的实例:# ^, W' D' [) F% E& j5 X
! M* o5 O0 x+ a6 _" D5 u: G
使用post方法压测% o. q9 l7 Y! r# r
5 `5 p! l7 S# p2 v7 L复制代码
* I! r9 q a) u. q7 @& _6 W! ^wrk.method = "POST"6 J* h! E. K8 T/ t V' h
wrk.headers["S-COOKIE2"]="a=2&b=Input&c=10.0&d=20191114***"0 F% |2 c3 z1 [# c
wrk.body = "recent_seven=20191127_32;20191128_111". T) u" b' p6 d7 z' y v
wrk.headers["Host"]="api.shouji.**.com"6 A A8 p4 W4 ~. x
" a' z( Q" M; Q% a; [
function response(status,headers,body)2 O. T, H! b6 F/ L0 v
if status ~= 200 then --将服务器返回状态码不是200的请求结果打印出来
5 q$ h5 l# u9 n* N print(body)$ z# `# Z. }" F$ @ }: w' O
-- wrk.thread:stop()8 ?! h/ U& q' q6 T9 ?; O( F+ K
end
% M* U q: v2 a% `0 r, o- e# j% Pend. n1 y' o2 v4 u: N
复制代码
+ J: q) e4 _' Y; T1 S, {1 ~0 A 5 c C* P( x" { S4 `
. V1 f9 `; G4 R2 X) ], s2 t6 R
发送json$ t/ ~5 ]* g( F" e
$ [' S+ u' Z% M" d& J; s复制代码
, Q0 N) x. k0 lrequest = function(), l) u1 J: [6 ^4 r
local headers = { }1 Q7 f( \0 L! a
headers['Content-Type'] = "application/json"1 ]# Q6 Q( v" v# ?# l. t: I
body = {0 Q- R" C x8 F! M+ P
mobile={"1533899828"},9 U6 q: T: h$ `4 O2 x
params={code=math.random(1000,9999)}) O* Q; [/ i9 \/ n/ ^3 [
}
# ]! a9 \' o% E2 P local cjson = require("cjson")
, A/ x% C& I7 j: s4 ^" S4 W+ o body_str = cjson.encode(body)
1 G- C6 L( P% c' [) Z6 @+ e return wrk.format('POST', nil, headers, body_str)
2 D- F1 u$ I1 J9 h+ cend
0 X. N8 b6 ~! K V/ S4 x8 Z复制代码
% ]: j- F4 C+ q; p' _/ X' G 2 x" r' M8 Z7 O! n, E% X$ q" J E
4 U8 y# R! U7 H7 M. @$ |$ v7 e8 Uwrk读取文件,实现随机header-cookie
1 `- T3 I, a4 H' U( o
3 R6 I5 U& x! i; |" k6 o9 \复制代码
, u; N7 D4 W$ E+ ]4 Y" eidArr = {}
/ a; [( p" n0 ^% m, }8 e+ c6 Cfalg = 0
. V5 C2 u1 G) j. l! twrk.method = "POST"1 h; I L' h @0 e
wrk.body = "a=1"
/ Z h: m5 |( Lfunction init(args)5 P1 O8 T4 D) H1 p5 Z! q# L
for line in io.lines("integral/cookies.txt") do1 u' g6 u d" b$ M+ w6 \" T
print(line)
) f, y q8 x$ H* k# u& l g idArr[falg] = line' N9 P' a7 f& j+ j. x ~
falg = falg+1, d" c# m& X% H* g
end3 V0 @: ^: q. z9 l" o7 i
falg = 02 T% v1 a% m9 _8 d1 k# ?
end5 T8 z4 V& {: u4 Y
' { p+ S& E3 \2 y: w* a
--wrk.method = "POST"0 Y' ]4 g: ^1 s7 c/ ^
--wrk.body = "a=1"
) j4 q; m) i4 X: s" h5 U1 }--wrk.path = "/v1/points/reading"
" }& c6 h( c: x/ X0 C- a
6 M' O5 p7 C9 N8 _$ U6 hrequest = function(). u7 h; ?! a M* n9 P- U+ w0 K
parms = idArr[math.random(0,4)] --随机传递文件中的参数7 B% M8 p. B3 A U
--parms = idArr[falg%(table.getn(idArr)+1)] 循环传递文件中的参数; _- k5 Z6 d g
wrk.headers["S-COOKIE2"] = parms
: V0 z& h5 E; Q+ j falg = falg+1
% c/ e2 f/ O0 e( U. V* { return wrk.format()/ f7 J$ l4 |1 K/ O6 X- P3 O/ s! d& b
end
9 D& L0 i8 r b5 K' ?) X, o复制代码
( S" s' s1 X' S3 V# N ! M5 E; A$ g$ K4 i, }0 J
' C" j, v5 m0 c3 W7 {( ], [0 e. e
wrk创建数组并初始化,拼接随机参数1 J# e# i' s- X8 [; k
2 D1 j1 h' ~; J复制代码
1 u7 Z& k7 R6 P: l% yidArr = {};! x% K8 e+ m6 E' L- B) L4 I4 [- ^& h/ h
function init(args)2 w8 L; w0 w' }" |# k* c
idArr[1] = "1";3 H8 |- Q% j" E- S6 z
idArr[2] = "2";
$ k7 `4 U0 T4 ` idArr[3] = "3";. N1 V* _" w0 d* d5 m
idArr[4] = "4";0 \5 I x, w5 W( X4 J+ r2 q
end! j% n, N0 a. f
0 @. E2 e* {8 A; ]! Y0 [4 w6 yrequest = function()8 {0 c4 |) a9 l9 x; p
parms = idArr[math.random(1,4)]
$ w! j8 w' g. j# v6 ` path = "/v1/points/reading?id="..parms
8 j7 d, B, v" c0 U1 N/ z* u return wrk.format("GET",path). _. f# ?/ F( P8 [, ]! Z; U8 H
end S4 {+ S2 b& s$ u9 g& V
复制代码 |
|