在做 Web 开发的时候,经常需要对 Web Page 或者 REST-ful API 做简单的 Benchmark。本文将介绍如何使用 cURL 进行简单快速的 Benchmark。
本文内容涉及:
- 使用
curl
查看加载时间 - 使用
curl -w
查看更多的网络情况 - 使用 Apache Benchmark(ab) 进行更高级的 Benchmark
当然,最后还会涉及实战应用。
使用 curl 查看加载时间
➜ blogs git:(curl) ✗ curl -s -w "%{time_total}\n" -o /dev/null http://www.github.com/
1.492
可以看到请求时间为 1.492
秒。此时:
-
-s, --silent
: 让 curl 保持静默模式,不会输出进度条 -
-w "%{time_total\n}"
:输出使用时间 -
-o /dev/null
: 这个参数用来隐藏 response 的内容
如果使用 time
可以看到 time_total
的细节:
➜ blogs git:(curl) ✗ time curl -s -o /dev/null http://www.github.com/
curl --silent -o /dev/null http://www.github.com/ 1.04s user 0.04s system 61% cpu 1.760 total
通常情况 Benchmark 一次的数据并不可靠,可以配合 for loop
发送多次请求:
# zsh shell
➜ blogs git:(curl) ✗ for i in {1..3}; curl -s -w "%{time_total}\n" -o /dev/null http://www.github.com/
0.665
0.678
1.399
curl
默认发送 GET
请求,可以使用 TL;DR 查看发送 POST, DELETE, PUT
或者更多的使用方法。
使用 curl -w
查看更多的网络情况
通常情况下一个 HTTP Request 会包含很多步骤,如果想知道 time_total
之外更详细的信息,可以参考 man curl
文章中, -w --write-out <format>
。
curl -w <format>
可以支持格式模板,我们可以使用 @template-file-name
的方式对输出格式进行自定义。
比如将这个模板保存为 curl-format.txt
\n
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_appconnect: %{time_appconnect}\n
time_pretransfer: %{time_pretransfer}\n
time_redirect: %{time_redirect}\n
time_starttransfer: %{time_starttransfer}\n
----------\n
time_total: %{time_total}\n
\n
➜ blogs git:(curl) ✗ curl -s -w "@curl-format.txt" -o /dev/null http://www.baidu.com/
time_namelookup: 1.101
time_connect: 1.130
time_appconnect: 0.000
time_pretransfer: 1.130
time_redirect: 0.000
time_starttransfer: 1.164
----------
time_total: 1.165
此时可以看到 DNS lookup, TCP 链接,数据传传输等信息。
使用 Apache Benchmark(ab) 进行更高级的 Benchmark
以上使用 curl
进行的简单的 Benchmark 都是基于单线程的访问。如果需要更多详细信息,或者希望进行并发测试,推荐使用 Apache Benchmark。
例如,我们希望对 github.com 做个 10 * Requests,3 * concurrency 的 Benchmark。
➜ blogs git:(curl) ✗ ab -n 10 -c 3 http://github.com/
This is ApacheBench, Version 2.3 <$Revision: 1748469 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking github.com (be patient).....done
Server Software:
Server Hostname: github.com
Server Port: 80
Document Path: /
Document Length: 0 bytes
Concurrency Level: 3
Time taken for tests: 3.293 seconds
Complete requests: 10
Failed requests: 0
Non-2xx responses: 10
Total transferred: 1030 bytes
HTML transferred: 0 bytes
Requests per second: 3.04 [#/sec] (mean)
Time per request: 987.954 [ms] (mean)
Time per request: 329.318 [ms] (mean, across all concurrent requests)
Transfer rate: 0.31 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 255 488 419.1 270 1282
Processing: 257 485 354.1 277 1179
Waiting: 257 485 354.1 277 1179
Total: 512 973 616.2 556 2196
Percentage of the requests served within a certain time (ms)
50% 556
66% 1125
75% 1537
80% 1640
90% 2196
95% 2196
98% 2196
99% 2196
100% 2196 (longest request)
此时我们可以得到更详细的 Benchmark 信息。
实战应用
最近项目的 Tech Leader 对一段代码提出了 Performance 的 issue。这段代码在 map
操作时做了大量的同步网络请求。代码大致如下:
data_set.map do |item|
# request 1 with item.property
# request 2 with item.property
# request 3 with item.property
end
Tead Leader 使用 curl
给出了简单的 Benchmark。
time curl -X "GET" URL
0.03s user 0.01s system 0% cpu 4.220 total
这段代码 data_set
大小至少为 20
,每一个 map
参考至少会发送 3
个网络请求。总网络请求数会达到 20 * 3 = 60
次,并且这些 HTTP Request 都以同步的方式进行。
因此希望采用异步的方式优化 map
操作。方案采用 Ruby 中的 parallel gem。
Parallel.map(data_set, in_threads: {thread_number}) do |item|
# request 1 with item.property
# request 2 with item.property
# request 3 with item.property
end
此时如何给出合理的 thread_number
,需要参考 Benchmark 结果。之后采用 Apache Benchmark
对 thread_number
进行挑选。
-
对 API 进行单次请求,确定 Benchmark baseline,之后的有话都必须比这个 baseline 快。
ab -n 1 URL
-
调整
thread_number
,分别对 API 进行多次同步请求,选出最佳thread_number
。ab -n 5 URL
-
调整
thread_number
,再对 API 进行 concurrency benchmark,选出最佳thread_number
。ab -n 15 -c 3 URL
分享一组数据:
Base line 为进行优化的时间:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 13076 13076 0.0 13076 13076
Waiting: 13075 13075 0.0 13075 13075
Total: 13076 13076 0.0 13076 13076
6 * Thread 优化之后的时间(Win):
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 2492 2492 0.0 2492 2492
Waiting: 2492 2492 0.0 2492 2492
Total: 2492 2492 0.0 2492 2492
10 * Thread 优化之后的时间:
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 6098 6098 0.0 6098 6098
Waiting: 6098 6098 0.0 6098 6098
Total: 6098 6098 0.0 6098 6098
由此数据可见, 6 * Thread 是最合适并行数。