前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux压测神奇wrk介绍

Linux压测神奇wrk介绍

原创
作者头像
windealli
发布2023-03-20 09:13:37
1.7K0
发布2023-03-20 09:13:37
举报
文章被收录于专栏:windealliwindealli

Linux压测神奇wrk介绍

简介

《wrk官方介绍》: wrk 是一种现代 HTTP 基准测试工具,能够在单个多核 CPU 上运行时产生大量负载。它将多线程设计与可扩展的事件通知系统(如 epoll 和 kqueue)相结合。 可选的 LuaJIT 脚本可以执行 HTTP 请求生成、响应处理和自定义报告。详细信息可在 SCRIPTING 中找到,几个示例位于 scripts/ 中。

wrk 定位

  • 轻量级性能测试工具
  • 仅支持 HTTP 协议
  • 仅支持单机压测,多机器压测需要每个机器都手动执行一次 wrk 命令
  • 不可取代 Jmeter、LR 等专业性能工具

基础用法

wrk --help

代码语言:shell
复制
[windealli@VM-52-29-centos workspace]$ wrk --help
Usage: wrk <options> <url>                            
  Options:                                            
    -c, --connections <N>  Connections to keep open  连接数 
    -d, --duration    <T>  Duration of test   压测时间        
    -t, --threads     <N>  Number of threads to use开启线程数  
                                                      
    -s, --script      <S>  Load Lua script file  使用lua脚本     
    -H, --header      <H>  Add header to request   
        --latency          Print latency statistics   
        --timeout     <T>  Socket/request timeout     
    -v, --version          Print version details      
                                                      
  Numeric arguments may include a SI unit (1k, 1M, 1G)
  Time arguments may include a time unit (2s, 2m, 2h)

基础示例

下列示例,使用wrk命令对http://127.0.0.1:80/sayHello?power=10进行压力测试。

代码语言:shell
复制
wrk -t12 -c1000 -d30s http://127.0.0.1:80/sayHello?power=10

其中:

-t12: 表示客户端启动了12个线程来进行压测。

-c1000: 表示打开了1000个连接

-d30s: 表示压测时间为30s

结果分析
代码语言:txt
复制
[windealli@VM-52-29-centos workspace]$ wrk -t12 -c1000 -d30s http://127.0.0.1:80
/sayHello?power=10
Running 30s test @ http://119.91.66.202/sayHello?power=10
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   172.14ms  317.41ms   1.99s    92.37%
    Req/Sec    88.75     96.55     2.39k    91.91%
  29616 requests in 30.10s, 36.59MB read
  Socket errors: connect 0, read 0, write 0, timeout 1278
Requests/sec:    983.92
Transfer/sec:      1.22MB
[windealli@VM-52-29-centos workspace]$ 

结果说明:

在30s的压测里,共发起了29616个请求,读到了36.59M的数据。

平均每秒请求983.92,Requests/sec: 983.92

平均延时: 172.14ms, 最大延迟1.99s。

wrk与luaJIT

wrk支持编写基于luaJIT的脚本来实现复杂的基准测试。

官方文档wrk/SCRIPTING介绍其使用方法。

wrk的脚本能力可以归纳为以下几部分:

  1. 定义了全局的talbe:wrk
  2. 提供了少量(三个)实现特定功能的API:wrk.format(),wrk.lookup(),wrk.connect
  3. 约定了一些函数接口给用户实现,wrk会在约定的阶段调用这些函数接口。

全局table与API

在wrk中,开发接口,由一个全局的table和一组全局函数组成:

全局table wrk
代码语言:txt
复制
 wrk = {
    scheme  = "http",
    host    = "localhost",
    port    = nil,
    method  = "GET",
    path    = "/",
    headers = {},
    body    = nil,
    thread  = <userdata>,
  }
function wrk.format(method, path, headers, body)
代码语言:lua
复制
-- wrk.format 根据入参构造一个http请求。入参会与全局table wrk合并, 
-- @param method : http method, 如POST,GET
-- @param method : 请求的路径
-- @parma headers : 请求要携带的headers, 是个table
-- @param body : 请求包体
-- @return : 返回一个请求包,可以在running阶段的request()中选择使用
function wrk.format(method, path, headers, body)
function wrk.lookup(host, service)
代码语言:lua
复制
-- wrk.lookup : 根据参数host和service返回所有一直的地址信息, 对应POSIX的 getaddrinfo() 函数
function wrk.lookup(host, service)
function wrk.connect(addr)
代码语言:lua
复制
-- wrk.connect : 校验地址addr能否连接成功
-- @param addr : 要测试的目标地址,必须是wrk.lookup返回的地址
-- @return : 可以连接成功,返回true,否则返回false。 
function wrk.connect(addr)

wrk 脚本运行的三个阶段

wrk支持在下面三个阶段运行luaJIT脚本

  • setup: 启动阶段,(目的IP已解析,执行测试的线程已经初始化,但还未开始执行)
  • running: 运行阶段,发送请求和接受应答。
  • done: 各个测试线程已经结束,获得测试结果各项数据的table

每个阶段提供了相应的一些函数接口,wrk测试运行时会在相应的阶段调用这些接口。

setupdone 阶段在独立的脚本运行环境中运行,不参与 running 过程. 因此,不共享全局变量。

setup 阶段

setup阶段 从目的IP被解析完成开始,到测试线程初始化完成(但还未启动)结束。

在setup阶段,约定了一下接口

代码语言:lua
复制
-- setup 每次线程启动时调用。
-- @param thread 用于表示当前线程的userdata对象。 
function setup(thread)

thread.addr      -- get or set the thread's server address
thread:get(name)  -- 从线程的环境变量中获取值
thread:set(name, value) -- 为线程设置环境变量,
thread:stop()          -- 停止线程。

因为function setup(thread)是在setup阶段调用,与running阶段在独立的脚本运行环境中。

不同的thread在此次共享全局变量。 我们可以在此处为各个编号。

running 阶段

running阶段从线程的初始化init()开始,然后就重复进行request()response()循环

running阶段约定了下列函数接口:

代码语言:lua
复制
-- init 线程初始化
-- @param args 用于接收用户传入的命令行参数
function init(args)

-- delay 用于指定下一个请求发送的延迟时间
-- @return 返回延迟时间,单位是ms
function delay()

-- request 用于构造当前要发送的HTTP request, 通过request,可以达到每次请求内容都不一样的效果。 但是,由于构造请求有一定开销,测试是最好在init()阶段就(通过wrk.format())预生成请求包,然后在此处快速轮询/选择。 
-- @return 要发送的HTTP request
function request()

-- response wrk收到请求应答时调用此方法,可以在此处对应答进行统计、分析等。
-- @param status : HTTP状态码
-- @param headers : 应答headers
-- @param body : 应答body
function response(status, headers, body)

done 阶段

当wrk各个thread测试完成(时间结束)是进入此阶段。

done阶段约定了下面接口

代码语言:lua
复制
-- done 测试完成是调用,根据根据入参进行此次基准测试的数据分析、展示
-- @params summary : 测试结果摘要(总请求数、各种错误指标等)
-- @params latency : 延迟相关的摘要信息。(最大延迟,最小延迟,平均延迟等)
-- @params requests : 测试结果摘要
function done(summary, latency, requests)

高级用法

发送一个POST请求

代码语言:lua
复制
-- post.lua
wrk.method="POST"
wrk.headers["Content-Type"] = "application/json"
wrk.body = "{\"UserId\":1001}"
代码语言:lua
复制
wrk  -t2 -c1000 -d10s -s test.lua http://127.0.0.1:8080/sayHello

构造不同请求,并随机发送

测试脚本:

代码语言:lua
复制
local index = 0

function init(args)
    -- 构造4个不同的请求
    req = {}
    req[0] = wrk.format("GET", "/?userId=1001", nil, nil) 
    req[1] = wrk.format("GET", "/?userId=1002", nil, nil) 
end

function request() 
    index = index + 1
    return req[index%2]
end

测试命令:

代码语言:lua
复制
wrk  -t2 -c1000 -d10s -s test.lua http://127.0.0.1:8080/

go编写的服务端程序:

代码语言:lua
复制
package main

import (
	"fmt"
	"net/http"
)

func handle(w http.ResponseWriter, r *http.Request) {
	userId := r.URL.Query().Get("userId")
	fmt.Printf("userId: %s\n", userId)
	w.Write([]byte("hello " + userId))
	// return
}

func main() {
	http.HandleFunc("/", handle)
	fmt.Println("http server start, :8080")
	http.ListenAndServe(":8080", nil)
}

统计不用应答的个数

压测脚本

代码语言:lua
复制
local count_thread = 0
local threads = {}


function setup(thread)
    thread:set("id", count_thread)
    table.insert(threads, thread)
    count_thread = count_thread + 1
end

function init(args)
    res_a = 0
    res_b = 0
end

function response(status, headers, body)
    if body == "a" 
    then
        res_a = res_a + 1
    else
        res_b = res_b + 1
    end
end

-- 统计应答为’a'和‘b'的数量
function done(summary, latency, requests)
    for index, thread in ipairs(threads) do
        local res_a = thread:get("res_a") -- 获取线程的res_a变量的值
        local res_b = thread:get("res_b") 
        print("res_a: " .. res_a)
        print("res_b: " .. res_b)
     end
end

Go编写的服务端程序:

代码语言:go
复制
func handle(w http.ResponseWriter, r *http.Request) {
	if time.Now().Unix()%5 == 0 {
		w.Write([]byte("a"))
	} else {
		w.Write([]byte("b"))
	}
}

func main() {
	http.HandleFunc("/", handle)
	fmt.Println("http server start, :8080")
	http.ListenAndServe(":8080", nil)
}

查看测试结果,可以看到b的响应数量是’a‘的4倍左右,符合预期。

代码语言:shell
复制
➜  wrk git:(main) ✗ wrk  -t2 -c1000 -d10s -s test.lua http://127.0.0.1:8080/
Running 10s test @ http://127.0.0.1:8080/
  2 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.92ms    3.79ms  39.29ms   74.21%
    Req/Sec    47.40k    13.73k  126.56k    82.63%
  921607 requests in 10.05s, 102.83MB read
  Socket errors: connect 0, read 1023, write 80, timeout 0
Requests/sec:  91722.38
Transfer/sec:     10.23MB
res_a: 92497
res_b: 372194
res_a: 93570
res_b: 363346
➜  wrk git:(main) ✗ 

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Linux压测神奇wrk介绍
    • 简介
      • wrk 定位
    • 基础用法
      • wrk --help
      • 基础示例
    • wrk与luaJIT
      • 全局table与API
      • wrk 脚本运行的三个阶段
      • setup 阶段
      • running 阶段
      • done 阶段
    • 高级用法
      • 发送一个POST请求
      • 构造不同请求,并随机发送
      • 统计不用应答的个数
相关产品与服务
云压测
云压测(Performance Testing Service, PTS)是一款分布式性能测试服务,可模拟海量用户的真实业务场景,全方位验证系统可用性和稳定性。支持按需发起压测任务,提供百万并发多地域流量发起能力。提供流量录制、场景编排、流量定制、高级脚本定制等功能,可快速根据业务模型定义压测场景,真实还原应用大规模业务访问场景,帮助用户提前识别应用性能问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档