目录
7.在 macOS 上,您可以使用Homebrew来安装wrk也可以下载源码make
1. wrk 是什么
wrk 是一个现代的 HTTP 基准测试工具,用于对 Web 服务器进行性能测试和负载测试。它可以模拟大量的并发连接,并测量服务器的响应时间、吞吐量和错误率等指标,帮助开发人员评估和优化 Web 服务器的性能。
2. 它的作用和优势
性能测试: wrk 可以帮助开发人员评估 Web 服务器在高并发情况下的性能表现,发现性能瓶颈和优化空间。
负载测试: 通过模拟大量并发连接,wrk 可以对服务器进行负载测试,以确定服务器在不同负载下的响应能力。
结果分析: wrk 提供详细的测试结果,包括平均响应时间、吞吐量、请求错误率等指标,帮助开发人员分析服务器的性能表现和瓶颈。
wrk 的主要特点和功能介绍:
1. 灵活的配置选项:
可以通过命令行参数灵活地配置并发连接数、测试持续时间、请求方式、请求头、请求体等参数,以满足不同的测试需求。
2. 高性能的并发模拟:
wrk 使用高效的事件驱动模型和多线程并发机制,可以模拟大量的并发连接,以确保测试结果的准确性和可靠性。
3. 详细的测试报告
wrk 提供详细的测试报告,包括平均响应时间、吞吐量、请求错误率等指标,以及请求响应时间分布图表,帮助开发人员全面了解服务器的性能表现。
4. 跨平台支持
wrk 支持在 Linux、Windows 和 macOS 等主流操作系统上运行,具有良好的跨平台兼容性。
5. 使用方便
wrk 的使用方法简单明了,可以通过命令行轻松启动测试,并且提供了丰富的文档和示例,方便开发人员快速上手和定制测试。
6.wrk在linux安装
命令行输入一下命令下载 wrk 源码
git clone https://github.com/wg/wrk.git
随后进入 wrk 目录并进行编译
cd wrk
make
随后将生成一个可执行的 wrk 文件,我们可以把这个文件拷贝到我想要的地方,或者直接拷贝到 bin 目录
cp wrk /usr/local/bin/
7.在 macOS 上,您可以使用Homebrew来安装wrk也可以下载源码make
brew install wrk
wrk -v或wrk
如果有以下显示,则说明安装成功了
wrk 4.2.0 [kqueue] Copyright (C) 2012 Will Glozer
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
-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)
8.使用wrk进行简单的性能测试
下面是一个使用wrk进行简单性能测试的示例:
wrk -t10 -c30 -d2s -T5s --latency http://www.baidu.com
Running 2s test @ http://www.baidu.com
10 threads and 30 connections
平均值 标准差 最大值 正负一个标准差占比
线程统计 Thread Stats Avg Stdev Max +/- Stdev
响应时间 Latency 200.85ms 192.21ms 1.08s 87.09
每线程每秒完成请求数Req/Sec 17.29 10.65 50.00 68.75%
Latency Distribution 延迟统计
50% 135.61ms 有50%的请求执行时间是在135.61ms内完成
75% 201.79ms 有75%的请求执行时间是在201.79ms内完成
90% 456.45ms 有90%的请求执行时间是在456.45ms内完成
99% 899.49ms 有99%的请求执行时间是在899.49ms内完成
322 requests in 2.05s, 3.25MB read 2秒执行了322个请求,读了3.25MB数据
Requests/sec: 157.42 每秒请求数
Transfer/sec: 1.59MB 每秒钟读取1.59MB数据量
在这个示例中:
-t10:指定10个线程。-c30:指定30个并发连接。-d2s:指定测试持续时间为2秒。-
-T5s:超时的时间 -
--latency:显示延迟统计 http://www.baidu.com:指定要测试的目标URL。
运行以上命令后,wrk将模拟10个线程,每个线程30个并发连接,持续2秒地向www.baidu.com发送HTTP请求,并记录测试结果,包括请求速率、平均响应时间、吞吐量等指标。
更多参数释义
-s 或 --script: 指定脚本路径
-H, --header: 添加http header, 比如. "User-Agent: wrk"
9.wrk压测复杂场景
定制化压力测试参数:
使用 wrk 进行高级压力测试时,可以通过调整以下参数来定制化压力测试:
线程数 (-t):使用 -t 参数可以指定并发连接数,即同时发起的请求线程数。例如,wrk -t20 表示使用 20 个线程进行测试。
连接数 (-c):使用 -c 参数可以指定每个线程的连接数,即每个线程同时保持的连接数。例如,wrk -c100 表示每个线程同时保持 100 个连接。
测试时长 (-d):使用 -d 参数可以指定测试的持续时间,以秒为单位。例如,wrk -d30 表示测试持续 30 秒。
请求速率 (-R):使用 -R 参数可以指定每秒钟发送的请求速率。例如,wrk -R1000 表示每秒发送 1000 个请求。
自定义请求头 (-H):使用 -H 参数可以添加自定义的 HTTP 请求头。例如,wrk -H "Authorization: Bearer token" 可以添加 Authorization 头。
wrk 还支持使用 Lua 脚本进行更复杂的测试场景。通过编写 Lua 脚本,可以实现更多定制化的功能和测试场景,例如:
模拟不同类型的请求:通过 Lua 脚本可以模拟不同类型的请求,包括 GET、POST、PUT 等,并携带不同的请求参数和请求体。
实现复杂的请求逻辑:可以在 Lua 脚本中实现复杂的请求逻辑,包括动态生成请求数据、处理响应数据等。
自定义报告输出:可以通过 Lua 脚本自定义报告输出格式,包括统计数据、图表等
示例:
wrk模拟下单请求,执行lua脚本
-- post.lua
math.randomseed(os.time())
function generate_uuid()
local template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
return string.gsub(template, '[xy]', function (c)
local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
return string.format('%x', v)
end)
end
local counter = 1
local threads = {}
function setup(thread)
-- 给每个线程设置一个 id 参数
thread:set("id", counter)
-- 将线程添加到 table 中
table.insert(threads, thread)
counter = counter + 1
end
-- thread table
local threads = {}
-- set up some variable for each thread
function setup(thread)
success_counter=0
thread:set("success_counter", success_counter)
table.insert(threads, thread)
end
-- record successful response
function response(status, headers, body)
success_counter = success_counter + 1
end
-- calculate tps
function done(summary, latency, requests)
--sum up successful response from each thread
total_success_counter = 0
for _, thread in ipairs(threads) do
total_success_counter = total_success_counter +thread:get("success_counter")
end
print("total_success_counter = " .. total_success_counter )
print("TPS = " .. 1000*1000*total_success_counter/summary.duration)
end
request = function()
local headers = {
["Content-Type"] = "application/json",
}
local body = {
version = "1.0",
signType = "MD5",
sign = "no",
reqTime = "1723623525",
mchNo = "M1711114860",
shopNo = "MS17111486018",
channelExtra = "{}",
mchOrderNo = generate_uuid(),
wayCode = "MOCK",
payScene = "1",
amount = 2,
currency = "CNY",
subject = "压测",
body = "压测内容",
notifyUrl = "https://xxx.xxxx.com"
}
-- 简单的JSON序列化函数
local function serialize(obj)
local lua = ""
for k, v in pairs(obj) do
lua = lua .. string.format("%q:%q,", k, v)
end
lua = string.sub(lua, 1, -2) -- 移除最后的逗号
return "{" .. lua .. "}"
end
-- 将table转换为JSON字符串
local body_json = serialize(body)
return wrk.format('POST', nil, headers, body_json)
end
wrk -t12 -c400 -d30s -s /Users/fei.y/wrk/post.lua --latency http://127.0.0.1:9216/api/test/order/mock
Running 30s test @ http://127.0.0.1:9216/api/test/order/mock
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.31s 10.61ms 1.65s 94.31%
Req/Sec 33.30 42.23 192.00 80.39%
Latency Distribution
50% 1.31s
75% 1.31s
90% 1.31s
99% 1.35s
4114 requests in 30.10s, 1.11MB read
Socket errors: connect 0, read 550, write 0, timeout 1478
Requests/sec: 136.69
Transfer/sec: 37.91KB
total_success_counter = 4114
TPS = 136.6932403332
统计脚本每一个线程执行,lua脚本
--count.lua
local counter = 1
local threads = {}
function setup(thread)
-- 给每个线程设置一个 id 参数
thread:set("id", counter)
-- 将线程添加到 table 中
table.insert(threads, thread)
counter = counter + 1
end
function init(args)
-- 初始化两个参数,每个线程都有独立的 requests、responses 参数
requests = 0
responses = 0
-- 打印线程被创建的消息,打印完后,线程正式启动运行
local msg = "thread %d created"
print(msg:format(id))
end
function request()
-- 每发起一次请求 +1
requests = requests + 1
return wrk.request()
end
function response(status, headers, body)
-- 每得到一次请求的响应 +1
responses = responses + 1
end
function done(summary, latency, requests)
-- 循环线程 table
for index, thread in ipairs(threads) do
local id = thread:get("id")
local requests = thread:get("requests")
local responses = thread:get("responses")
local msg = "thread %d made %d requests and got %d responses"
-- 打印每个线程发起了多少个请求,得到了多少次响应
print(msg:format(id, requests, responses))
end
end
wrk -t12 -c400 -d30s -s /Users/fei.y/wrk/count.lua --latency http://127.0.0.1:9216/api/test/count/mock
thread 1 created
thread 2 created
thread 3 created
thread 4 created
thread 5 created
thread 6 created
thread 7 created
thread 8 created
thread 9 created
thread 10 created
thread 11 created
thread 12 created
Running 30s test @ http://127.0.0.1:9216/api/test/count/mock
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 484.03ms 496.50ms 2.00s 83.41%
Req/Sec 16.48 11.88 100.00 68.57%
Latency Distribution
50% 188.42ms
75% 767.67ms
90% 1.25s
99% 1.93s
4862 requests in 30.10s, 50.13MB read
Socket errors: connect 0, read 0, write 0, timeout 684
Requests/sec: 161.54
Transfer/sec: 1.67MB
thread 1 made 443 requests and got 409 responses
thread 2 made 507 requests and got 474 responses
thread 3 made 372 requests and got 339 responses
thread 4 made 487 requests and got 454 responses
thread 5 made 526 requests and got 493 responses
thread 6 made 323 requests and got 291 responses
thread 7 made 289 requests and got 256 responses
thread 8 made 420 requests and got 387 responses
thread 9 made 450 requests and got 417 responses
thread 10 made 413 requests and got 380 responses
thread 11 made 383 requests and got 350 responses
thread 12 made 645 requests and got 612 responses
更多脚本示例请参考
https://github.com/wg/wrk/tree/master/scripts
分析和优化压力测试结果
1. 解读wrk测试结果:
吞吐量 (Throughput):表示服务器在单位时间内处理的请求数量,通常以请求数/秒 (Requests per Second, RPS) 表示。吞吐量越高,服务器性能越好。
平均响应时间 (Average Response Time):表示服务器平均处理每个请求所花费的时间,通常以毫秒为单位。平均响应时间越低,服务器响应速度越快。
延迟百分比 (Latency Percentiles):表示在一定时间范围内的延迟分布情况,通常以百分比表示。例如,50% 的请求在多少毫秒内完成,90% 的请求在多少毫秒内完成等。
错误率 (Error Rate):表示测试过程中出现错误的请求比例,通常以百分比表示。错误率越低,服务器稳定性越好。
10.根据测试结果优化应用性能
代码层面优化:
- 减少不必要的对象创建:使用对象池、静态工厂方法等方式减少对象创建的开销。
- 优化数据结构:使用更适合当前场景的数据结构,比如使用
ArrayList代替LinkedList,在特定场景下可以提高性能。 - 算法优化:选择更高效的算法,减少计算复杂度。
- 减少锁的竞争:使用并发数据结构,比如
ConcurrentHashMap,或者减少同步块的大小,使用读写锁代替独占锁。 - 使用并发编程:合理使用线程池,充分利用多核CPU的优势。
- 异步日志:缩减打印日志,核心日志打印
JVM优化:
- 垃圾回收调优:选择合适的垃圾回收器(如CMS、G1、ZGC等),调整垃圾回收相关的参数。
- 堆内存优化:合理分配堆内存大小,避免频繁的Full GC。
- 栈内存调整:适当调整线程栈大小,减少内存开销。
架构层面优化:
- 缓存:使用缓存减少对数据库的直接访问,如Redis、Memcached,本地缓存等,构建多级缓存
- 数据库优化:优化SQL语句,使用索引,分库分表,读写分离等。
- 负载均衡:通过负载均衡分散请求到多个服务器,提高系统整体的TPS。
- 异步处理:使用消息队列等异步处理方式,可以提高系统的吞吐量。
硬件层面优化:
- 增加内存:增加服务器的内存,可以减少内存交换,提高处理速度。
- 使用SSD:使用SSD代替HDD,可以显著提高I/O性能。
- 网络优化:提高网络带宽,减少网络延迟。
其他优化:
- 代码级别分析:使用Java性能分析工具(如JProfiler、VisualVM)找出性能瓶颈。
- 系统监控:持续监控系统性能,及时发现并解决问题。
- 压力测试:通过压力测试找出系统的极限,并根据测试结果进行优化。
在进行优化时,应该综合考虑性能瓶颈出现在哪个层面,然后有针对性地进行优化。优化过程中,应该密切监控系统的各项性能指标,确保优化措施是有效的,并不会引入新的问题。
2419

被折叠的 条评论
为什么被折叠?



