brpc benchmark_http 压测工具:用 brpc 高性能 HTTP 客户端压测 HTTP 服务极限
【免费下载链接】brpcbrpc is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. "brpc" means "better RPC".项目地址: https://gitcode.com/GitHub_Trending/brpc/brpc
benchmark_http 是 brpc 仓库 example/http_c++ 目录下自带的一个 HTTP 压测工具。它本质上就是一个基于 brpc HTTP client 的多线程压测程序:通过 gflags 定义压测参数,用 brpc::Channel 作为发压通道,用 bvar::LatencyRecorder 实时统计 QPS 与延迟,可以在命令行上替代 ab 完成 HTTP server 的极限性能测试。读完本文,你将掌握 benchmark_http 的编译、启动、核心参数配置、发压原理与结果解读,以及它相对于 ab 的优势与适用边界。
为什么需要 benchmark_http:ab 的瓶颈与 brpc 的答案
ApacheBench(ab)功能较多但年代久远,在压测超高吞吐的 HTTP server 时,压测工具本身有时反而会成为瓶颈——当服务端 QPS 高到一定程度,ab 自身的连接管理、线程模型与统计逻辑会先于被测服务打满,导致测出的"极限性能"实际上是 ab 的极限而非服务器的极限。
benchmark_http 的定位就是解决这个问题:它基本上就是一个 brpc http client,性能很高,功能较少,一般压测够用了。它继承了 brpc 在 I/O 线程模型、连接池管理、bthread 调度等方面的优化,可以把更多 CPU 花在真正发起请求上,从而压出被测 HTTP server 的真实上限。
编译 benchmark_http
benchmark_http 不是一个独立的工具,而是随 brpc 的 http 示例一起编译的。按以下步骤操作:
- 先完成 brpc 的下载和编译(支持 config_brpc.sh、CMake、Bazel 等多种构建方式);
- 进入
example/http_c++目录编译; - 编译成功后即可看到
benchmark_http可执行文件。
以 Makefile 方式为例:
$ cd example/http_c++ $ make $ ls benchmark_http http_server http_client以 CMake 方式为例(CMakeLists 中通过brpc_example_configure_target(benchmark_http)注册了该目标,见 example/http_c++/CMakeLists.txt):
$ cd example/http_c++ $ cmake -B build && cmake --build build -j4 $ ls build/benchmark_http编译产物默认静态链接libbrpc.a(Makefile 中STATIC_LINKINGS += -lbrpc);如需动态链接,可先make clean再执行LINK_SO=1 make(CMake 对应-DLINK_SO=ON)。示例还默认开启了 CPU profiler 支持(-DBRPC_ENABLE_CPU_PROFILER),方便压测时定位客户端自身热点。
提示:
example/http_c++目录同时提供了http_server(被测服务样例)与http_client(单次 HTTP 访问客户端),benchmark_http 可与它们配合,在本地快速搭建一套"服务端 + 压测"环境。
核心参数总览
benchmark_http 的所有压测参数都通过 gflags 定义在 example/http_c++/benchmark_http.cpp,可在启动时用-参数名=值的方式传入。完整参数如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
-url | string | 0.0.0.0:8010/HttpService/Echo | 被测服务器地址与路径,格式为host:port/path |
-thread_num | int32 | 50 | 发压线程数 |
-use_bthread | bool | false | 是否使用 bthread(协程)代替 pthread 发压 |
-connection_type | string | 空 | 连接方式:single(单连接)、pooled(连接池)、short(短连接);为空则用协议默认方式 |
-protocol | string | http | 客户端协议,对应 ChannelOptions.protocol |
-data | string | 空 | 非空时以 POST 方式把该数据作为 body 发给服务器 |
-load_balancer | string | 空 | 负载均衡算法名,用于多 server 地址场景 |
-timeout_ms | int32 | 100 | 单次 RPC 超时时间(毫秒) |
-max_retry | int32 | 3 | 最大重试次数(不含首次 RPC) |
-dont_fail | bool | false | 为 false 时,只要有调用失败就触发 CHECK 致命错误并退出 |
-dummy_port | int32 | -1 | 大于等于 0 时在该端口启动 dummy server,用于暴露内置监控服务 |
基本用法示例
压测本机http_server样例的 Echo 服务(默认监听 8010 端口):
$ ./benchmark_http -url=0.0.0.0:8010/HttpService/Echo -thread_num=50以 POST 方式压测并携带 body:
$ ./benchmark_http -url=0.0.0.0:8010/HttpService/Echo -data='hello world'压测时直接观察 QPS 与延迟:
$ ./benchmark_http -url=0.0.0.0:8010/HttpService/Echo -thread_num=100 -timeout_ms=1000工具启动后每秒打印一次实时统计(见下节"输出解读"),按 Ctrl-C(SIGINT)退出并输出退出日志。
源码剖析:benchmark_http 是如何发压的
1. 构造 Channel:一条线程安全、可共享的通信线
main()中首先创建brpc::Channel与brpc::ChannelOptions,把-protocol与-connection_type映射到 Channel 选项上,再调用channel.Init(url, load_balancer, &options)完成初始化(benchmark_http.cpp#L79-L91):
brpc::Channel channel; brpc::ChannelOptions options; options.protocol = FLAGS_protocol; // 默认 "http" options.connection_type = FLAGS_connection_type; // single/pooled/short/空 if (channel.Init(FLAGS_url.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) { LOG(ERROR) << "Fail to initialize channel"; return -1; }关于三种连接方式的行为差异,可参考 docs/cn/client.md#连接方式:
- single(单连接):一个远端地址只建一条连接,所有请求复用,省去建连开销;
- pooled(连接池):为单个远端维护连接池,池容量上限由
-max_connection_pool_size控制(默认 100);需要时若无空闲连接则新建,归还时若池已满则直接关闭; - short(短连接):每次 RPC 前建连、结束后关闭,有固定建连开销,一般只用于偶尔发起的操作。
-url为空时框架会按协议选择默认连接方式。注意:-url同时承载了地址与路径,channel.Init会对多地址配合-load_balancer做初始化;而对单个地址,brpc 会将其解析为可用的 endpoint。
2. 创建发压线程:pthread 还是 bthread
根据-use_bthread标志,工具选择用 pthread 还是 bthread 创建-thread_num个发送者线程,所有线程共享同一个 Channel(benchmark_http.cpp#L93-L112):
if (!FLAGS_use_bthread) { pids.resize(FLAGS_thread_num); for (int i = 0; i < FLAGS_thread_num; ++i) pthread_create(&pids[i], nullptr, sender, &channel); } else { bids.resize(FLAGS_thread_num); for (int i = 0; i < FLAGS_thread_num; ++i) bthread_start_background(&bids[i], nullptr, sender, &channel); }sender是每个线程的执行体:在!brpc::IsAskedToQuit()循环内不断构造brpc::Controller、设置超时与重试、填写http_request().uri(),然后同步调用channel.CallMethod(...)(done 为 nullptr 即同步等待响应或错误返回),成功时把cntl.latency_us()写入全局的bvar::LatencyRecorder(benchmark_http.cpp#L41-L73)。
关键设计点:
- 同步调用 + 栈上 Controller:因为同步等待返回,
cntl可直接放在栈上,无需异步回调,逻辑简单清晰; - 失败节流:当请求失败(如无法连接服务器)时,线程会
bthread_usleep(100000)(100ms)再继续,避免失败场景下空转打满 CPU;这也是源码注释明确说明的"针对此压测工具的特殊休眠"; - 退出机制:
brpc::IsAskedToQuit()在收到 Ctrl-C 后返回 true,主循环随之退出,随后 join 所有线程并打印退出日志。
3. 实时统计:bvar::LatencyRecorder 与 QPS
全局对象bvar::LatencyRecorder g_latency_recorder("client")(benchmark_http.cpp#L39)负责统计所有成功的请求。主线程每秒打印一次:
LOG(INFO) << "Sending " << FLAGS_protocol << " requests at qps=" << g_latency_recorder.qps(1) << " latency=" << g_latency_recorder.latency(1);qps(1):最近 1 秒的每秒请求数;latency(1):最近 1 秒的平均延迟。
由于 bvar 统计是增量式的,长时间运行时只需每秒取一次快照即可得到近乎实时的吞吐与延迟曲线;配合-dummy_port启动的 dummy server,还可以通过 brpc 内置服务(如/vars)在线观察这些指标,详见 docs/cn/builtin_service.md。
4. 输出解读示例
典型的运行输出形如:
INFO ... Sending http requests at qps=283671 latency=169 INFO ... Sending http requests at qps=291054 latency=166 INFO ... Sending http requests at qps=289322 latency=168 INFO ... benchmark_http is going to quit其中 qps 为最近 1 秒吞吐,latency 为最近 1 秒平均延迟(微秒)。若出现error=...与 CHECK 失败,说明存在超时或连接失败;当-dont_fail=false时此类失败会直接使进程以错误退出。
参数调优实战建议
1. 用 -thread_num 控制并发度
-thread_num决定同时在途的请求数,是压测并发度的直接来源。建议从较小值(如 10)逐步增大,观察 QPS 是否继续上涨:若 QPS 不再随线程数增长甚至下降,说明已接近服务端或客户端瓶颈。注意:工具使用同步阻塞式调用,一个线程同一时刻只有一个在途请求,因此线程数 ≈ 最大并发连接数(连接池模式下还会受到-max_connection_pool_size约束)。
2. 用 -use_bthread 榨取客户端剩余性能
bthread 是 brpc 的 M:N 协程实现(参见 docs/cn/bthread.md)。当单机核数有限、pthread 数量过大导致上下文切换成本高时,-use_bthread=true可以在更少的系统线程上承载更多并发请求,适合追求客户端侧极限吞吐的场景。
3. 用 -connection_type 模拟不同连接模型
- 压测"长连接友好"的服务(如 HTTP/1.1 keep-alive),用
-connection_type=single或-connection_type=pooled; - 想模拟短连接场景,用
-connection_type=short,但要意识到建连开销会显著拉低 QPS; - 不指定(留空)则使用 http 协议默认方式,适合快速上手。
4. 用 -timeout_ms 与 -max_retry 控制压测语义
-timeout_ms默认仅 100ms,压测高延迟服务前务必调大(如 1000~3000),否则大量请求会因超时失败,造成误判;-max_retry默认 3,表示失败后最多重试 3 次(不含首次)。压测中重试会放大服务器压力,若只想测量"原始请求成功率",可设为 0。
5. 用 -dummy_port 挂载内置监控
$ ./benchmark_http -url=0.0.0.0:8010/HttpService/Echo -dummy_port=8088此时可访问http://localhost:8088/vars查看 client 侧 bvar 指标(qps、延迟分位等),也可以在压测过程中用 brpc 内置的 rpc_view、rpcz 等工具做更细致的观测(参见 docs/cn/rpc_view.md)。
benchmark_http vs ab:怎么选
| 维度 | ab | benchmark_http |
|---|---|---|
| 本质 | 独立的 ApacheBench 程序 | 基于 brpc 的 HTTP 客户端压测程序 |
| 性能上限 | 高并发下工具自身可能成为瓶颈 | 复用 brpc 高性能 I/O 与连接池,极限更高 |
| 功能丰富度 | 功能较多 | 功能较少,但一般压测够用 |
| 参数模型 | -n/-c 等 ab 风格 | gflags 风格(-thread_num/-connection_type 等) |
| 统计输出 | 汇总报告 | 每秒实时打印 QPS 与平均延迟,可挂内置监控 |
适用建议:做常规功能验证或低频压测可用 ab;追求 HTTP server 极限性能测试、希望压测工具自身不成为瓶颈时,优先使用 benchmark_http。
更进一步:把 benchmark_http 的思想搬进自己的服务
benchmark_http 的源码结构本身就是"如何用 brpc 写一个高并发 HTTP 客户端"的极佳范例,example/http_c++/benchmark_http.cpp 中可直接借鉴的模式包括:
- 用 gflags 定义全部可调参数,便于命令行/脚本化压测;
- Channel 线程安全、全局共享,线程只需各自持有 Controller;
- 同步调用时 Controller 放栈上,配合
IsAskedToQuit()实现优雅退出; - 用
bvar::LatencyRecorder做无锁化的高并发统计; - 失败时 sleep 节流,避免空转。
如果你的压测场景需要更复杂的请求构造(如带 header、Cookie、分块上传),可直接扩展sender中对cntl.http_request()的填充逻辑;需要多机分布式压测时,还可参考 docs/cn/rpc_press.md 中 rpc_press 的多机压测思路。
总结
- benchmark_http 是 brpc 仓库自带的 HTTP 压测工具,本质是一个多线程的 brpc HTTP client,用于替代 ab 测试 HTTP server 的极限性能;
- 编译入口在 example/http_c++,与 http_server/http_client 示例一同产出;
- 核心参数集中在 benchmark_http.cpp#L27-L37:
-url、-thread_num、-use_bthread、-connection_type、-protocol、-data、-timeout_ms、-max_retry、-dummy_port等; - 发压原理为:共享
brpc::Channel+ 多线程/多协程同步调用 +bvar::LatencyRecorder实时统计,每秒打印 QPS 与平均延迟; - 优势在于复用 brpc 的高性能客户端实现,压测时工具自身不容易成为瓶颈;代价是功能较少,复杂压测场景(如分布式多机压测)可转向 rpc_press 等工具。
【免费下载链接】brpcbrpc is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. "brpc" means "better RPC".项目地址: https://gitcode.com/GitHub_Trending/brpc/brpc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考