- RPC框架
- 后端
- 微服务
- 网络
- 通信
【免费下载链接】brpc
brpc 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".
brpc 默认集成 bvar 计数器库,所有进程内暴露(expose)的 bvar 都可以通过内置服务/vars以 HTTP 方式查询。本文以官方文档 docs/en/vars.md 为主线,结合 src/bvar 与 src/brpc/builtin/vars_service.cpp 的源码实现,讲解如何用精确名、通配符检索 bvar、查看历史趋势曲线,以及如何计算和解读 CDF / 百分位延迟曲线,最终帮助你用这些指标定位性能长尾、保障 SLA。
bvar 是什么:为什么值得监控
bvar 是一套面向多线程应用的高性能计数器库,用于方便地记录和查看各类统计信息。其核心设计是通过**线程局部存储(TLS)**降低缓存一致性开销(cache bouncing):每个线程只写自己私有的数据区,不参与全局原子竞争,读取时再把所有线程的数据合并。因此在高度竞争的场景下,bvar 的写入开销远低于传统原子操作,也明显快于百度内部的旧版统计库 UbMonitor。
这一设计也决定了它的适用边界:bvar 把写入时的竞争转移到了读取端——读取需要合并所有线程写下的数据,比普通读取慢得多。所以文档明确指出:如果某个计数器的读写都很频繁,或者需要基于最新值立刻做决策,就不应使用 bvar。它最适合低频率的日志输出、监控展示这类"写多读少"的场景。
brpc 自身大量使用 bvar 暴露内部状态,例如:
bthread_creation_count : 125134 bthread_creation_latency : 3 bthread_creation_latency_50 : 3 bthread_creation_latency_90 : 5 bthread_creation_latency_99 : 7 bthread_creation_latency_999 : 12 bthread_creation_latency_9999 : 12 bthread_creation_latency_cdf : "click to view" bthread_creation_latency_percentiles : "[3,5,7,12]" bthread_creation_max_latency : 7 bthread_creation_qps : 100 bthread_group_status : "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " bthread_num_workers : 24 bthread_worker_usage : 1.01056此外 bvar 还默认提供以process_/system_为前缀的进程级、系统级指标(见 docs/en/bvar.md):
process_context_switches_involuntary_second : 14 process_cpu_usage : 0.428 process_cpu_usage_user : 0.286 process_disk_write_bytes_second : 260902 process_memory_resident : 392744960 system_core_count : 12 system_loadavg_1m : 0.000如果你正在为应用寻找一个采集与展示指标的方案,bvar 值得优先考虑——brpc 服务天然将其暴露,无需额外埋点即可看到大量内部状态。
/vars 查询方法:精确名、多名称与通配符
只要服务是 brpc server(内置服务默认开启),就可以通过以下 URL 查询 bvar:
| 查询方式 | 示例 | 说明 |
|---|---|---|
| 列出全部 | /vars | 列出所有已暴露的 bvar |
| 按名称查询 | /vars/rpc_socket_count | 列出名为rpc_socket_count的 bvar |
| 按多个名称查询 | /vars/pid;process_cpu_usage;rpc_controller_count | 同时列出多个名称(用;、,或空格分隔) |
| 通配符匹配 | /vars/rpc_server*_count;iobuf_blo$k_* | 按通配符模式匹配一组 bvar |
关于通配符有一个易错点:$匹配单个字符,而?是 URL 中的保留字符不能直接使用。这一点在源码中也有印证——src/brpc/builtin/vars_service.cpp 在构造bvar::DumpOptions时将question_mark显式设置为'$',white_wildcards则取自 HTTP 请求的未解析路径(unresolved_path),随后调用bvar::Variable::dump_exposed()完成过滤输出。
通配符的过滤行为由 src/bvar/variable.cpp 中的bvar_dump_include/bvar_dump_exclude相关机制同一套代码支撑(DumpOptions同时支持白名单与黑名单通配符),所以 URL 查询与文件 dump 的匹配语义是一致的。
/vars页面左上角还提供了一个搜索框:输入名称片段即可实时定位 bvar,多个模式之间用,、:或空格分隔,搜索请求会实时回发到服务端并刷新列表(对应vars_service.cpp中toURL()/search()这段前端逻辑)。你可以把带通配符的 URL 直接复制粘贴分享给他人,对方会看到与你相同的 bvar 列表(数值可能随时间变化)。
在终端用 curl 查询 /vars
/vars同样支持命令行访问。浏览器请求时会返回 HTML 页面(服务端通过UseHTML()判断并设置text/html内容类型),而 curl 等终端工具默认得到纯文本输出(text/plain),格式为每行一条name : value:
$ curl brpc.baidu.com:8765/vars/bthread* bthread_creation_count : 125134 bthread_creation_latency : 3 bthread_creation_latency_50 : 3 bthread_creation_latency_90 : 5 bthread_creation_latency_99 : 7 bthread_creation_latency_999 : 12 bthread_creation_latency_9999 : 12 bthread_creation_latency_cdf : "click to view" bthread_creation_latency_percentiles : "[3,5,7,12]" bthread_creation_max_latency : 7 bthread_creation_qps : 100 bthread_group_status : "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 " bthread_num_workers : 24 bthread_worker_usage : 1.01056注意上述示例中的brpc.baidu.com:8765是官方演示服务地址,实际使用时应替换为你自己的 brpc 服务地址与端口(例如127.0.0.1:8000/vars/...)。从源码看,/vars返回时还会启用 gzip 压缩(cntl->set_response_compress_type(COMPRESS_TYPE_GZIP)),所以即便 bvar 数量很大,传输开销也可控。
除了网页与 curl,还可以给 URL 追加?series参数:VarsService::default_method()会检测到series查询参数后调用bvar::Variable::describe_series_exposed(),以application/json返回该 bvar 的时序采样数据,这正是前端绘图所依赖的数据接口(src/brpc/builtin/vars_service.cpp)。
查看历史趋势:60 秒、60 分钟、24 小时与 30 天
大多数数值型 bvar 都是可点击的。点击后页面会展示该指标的历史趋势曲线,每个可点击的 bvar 会记录最近60 秒、60 分钟、24 小时、30 天四个时间尺度的采样值,合计174个数值。
这 174 个数值并非估算,而是源码中实实在在的数据布局。在 src/bvar/detail/series.h 中,序列数据被保存在定长数组里:
T _array[60 + 60 + 24 + 30];其中前 60 格存秒级样本,接着 60 格存分钟级、24 格存小时级、30 格存天级。正因为内存占用是固定的,文档才给出"1000 个可点击的 bvar 大约占用 1M 内存"的估算(每个 bvar 约 1KB,174 × 若干字节/样本)。
这些序列由采样器线程周期驱动:bvar::detail::Sampler的take_sample()会被一个专用线程每秒调用一次(见 src/bvar/detail/sampler.h),ReducerSampler会把每秒的快照压入有界队列BoundedQueue,后续按窗口尺寸求差值或求和即可得到任意时间窗的统计值。这也解释了为什么Window<>类指标在极端情况下会有一秒的延迟——它们的数据来自每秒一次的采样。
计算与查看百分位数(Percentile)
百分位数的含义
x-ile(x 百分位)指一组有序数值中位于N × x%位置的值。例如时间窗口内有 1000 个延迟样本,升序排列后:
- 第 500 个值(1000 × 50%)即 50-ile,也就是中位数;
- 第 990 个值(1000 × 99%)是 99-ile;
- 第 999 个值(1000 × 99.9%)是 99.9-ile。
相比平均值,百分位数能揭示延迟的真实分布,对分析系统行为更准确。工业级服务通常对 SLA 有硬性要求——例如百度内部二级服务要求不低于 99.97%,一级服务不低于 99.99%——即使平均延迟很好,糟糕的长尾区域仍可能击穿 SLA。百分位数正是分析长尾区域最直接的武器。
CDF 曲线:看分布形状
百分位数可以绘制成 CDF(累积分布函数)曲线或"百分位随时间变化"曲线。
CDF 图的 X 轴是比例(排序位置 / 总数),Y 轴是对应的百分位值。例如 X=50% 对应的 Y 值就是 50-ile。如果系统要求"99.9% 的请求要在 Y 毫秒内处理完",就应该检查 X=99.9% 处的 Y 值。
为什么叫 CDF?因为任取一个 Y=y,其对应的 X 表示"值 ≤ y 的比例"。由于样本是(近似均匀)随机采样,这个比例可以看作"值 ≤ y 的概率",即 P(values ≤ y),这正是 CDF 的定义。CDF 的导数即 PDF(概率密度函数):把 CDF 的 Y 轴切分成许多小段,取每段两端 X 值的差作为新 X 轴,就能得到类似正态分布旋转 90° 的 PDF 曲线。但中位数附近的密度往往远高于其他区域,会把长尾压得非常平、难以阅读,因此系统更倾向于用 CDF 而非 PDF 展示分布。
判断 CDF 曲线好坏的两条简单规则:
- 越平越好:理想 CDF 是水平直线,意味着没有等待、拥塞或停顿——实践中几乎不可能;
- 99% 到 100% 之间的面积越小越好:99% 右侧就是长尾区域,对 SLA 影响显著。
实践中,缓慢上升且长尾面积小的 CDF 曲线是最理想的形态。
百分位随时间曲线:定位性能回退
百分位随时间曲线包含四条曲线:X 轴是时间,Y 轴从上到下分别是 99.9%、99%、90%、50% 分位值,颜色由橙到黄逐渐变浅。
鼠标悬停可以看到对应时刻的值。例如图上 tooltip 的含义是"39 秒前的 99% 延迟分位为 330 微秒"。该图刻意不包含 99.99-ile 曲线,因为它通常显著高于其他曲线,会把其他曲线压得难以阅读;如需单独查看 99.99-ile,可以点击名称以_latency_9999结尾的 bvar。这类图展示了分位数随时间的变化,非常适合分析系统的性能回退(performance regression)。
brpc 自动计算的延迟指标
brpc 会自动为服务计算延迟分布,无需用户手工添加。这些指标会出现在/vars中,例如:
从源码看,这套指标由bvar::LatencyRecorder支撑。src/bvar/latency_recorder.h 显示它内部聚合了多个子 bvar:
_latency(IntRecorder):平均延迟;_max_latency(Maxer<int64_t>):最大延迟;_latency_percentile(Percentile):分位数原始数据;_latency_p1/_p2/_p3/_999/_9999(PassiveStatus<int64_t>):50%、75%(p2/p3 由bvar_latency_p1/p2/p3三个 gflag 决定具体分位)、99.9%、99.99% 分位值;_latency_cdf(CDF):可绘制的 CDF 数据;_latency_percentiles(PassiveStatus<Vector<int64_t, 4>>):分位数组。
expose(prefix)后会生成以prefix为前缀的一族变量,如xxx_latency、xxx_max_latency、xxx_count、xxx_qps、xxx_latency_percentiles、xxx_latency_cdf等(见 src/bvar/latency_recorder.h 的注释与实现)。百分位底层基于 src/bvar/detail/percentile.h 的PercentileInterval:延迟被采样进固定大小的区间,查询时升序排序后按下标取值(get_sample_at),并通过"合并时每个样本以近似等概率保留"的策略在内存与精度之间取得平衡。
bvar::LatencyRecorder可以对任意代码段计算延迟分布,用法如下(详见 docs/en/bvar_c++.md):
#include <bvar/bvar.h> ... bvar::LatencyRecorder g_latency_recorder("client"); // expose 该 recorder ... void foo() { ... g_latency_recorder << my_latency; // 记录一次延迟 ... }如果应用已经启动了 brpc server,就可以在/vars中看到client_latency、client_latency_cdf等指标,点击即可查看动态刷新的曲线:
非 brpc server 场景:dummy server
如果你的程序只使用 brpc client,甚至完全不使用 brpc,但仍想通过网页查看 bvar 曲线,有两种方式(见 docs/cn/dummy_server.md):
仅使用 brpc client:在程序运行目录下创建一个名为
dummy_server.port的文件,内容写一个端口号(如 8888)。程序启动后会自动在该端口起一个空 server(dummy server),进程内所有 bvar 都可以通过其内置服务查看。完全不用 brpc:需要手动添加 dummy server。先参考 docs/en/getting_started.md 完成编译接入,然后在程序入口加入:
#include <brpc/server.h> ... int main() { ... brpc::StartDummyServerAt(8888/*port*/); ... }延伸:将 bvar 导出到 Prometheus 与本地文件
/vars只是 bvar 消费方式的一种。bvar 还支持把指标导出到 Prometheus:将 Prometheus 抓取目标的路径设为/brpc_metrics即可。例如 brpc server 运行在localhost:8080,则抓取地址配置为127.0.0.1:8080/brpc_metrics。其实现位于 src/brpc/builtin/prometheus_metrics_service.cpp,通过PrometheusMetricsDumper将 bvar 输出为 Prometheus 文本格式。
同时 bvar 支持按固定周期把全部已暴露变量写入本地文件(默认关闭)。控制它的 gflag 定义在 src/bvar/variable.cpp:
| gflag | 默认值 | 作用 |
|---|---|---|
bvar_dump | false | 开启后台线程周期性 dump 所有 bvar;关闭时所有bvar_dump_*均不生效 |
bvar_dump_exclude | "" | 按逗号分隔的通配符排除某些 bvar,空表示不排除 |
bvar_dump_file | monitor/bvar.<app>.data | dump 输出的文件路径 |
bvar_dump_include | "" | 按逗号分隔的通配符只保留匹配的 bvar,空表示全部 |
bvar_dump_interval | 10 | 两次 dump 之间的间隔(秒) |
bvar_dump_prefix | <app> | 每个 dump 出的名字附加的前缀 |
bvar_dump_tabs | latency=*_latency*... | 按过滤器把 bvar 分到不同的 tab(格式:tab_name=wildcards,分号分隔) |
注意这些 gflag 大多注册了校验器/回调(validate_bvar_dump、wakeup_dumping_thread),建议通过命令行参数-bvar_dump=true或google::SetCommandLineOption()动态修改,而不要直接赋值FLAGS_bvar_dump_file等 std::string 类型的 gflag——前者会触发校验与唤醒 dump 线程,后者既存在线程安全问题也不会让导出线程生效。dump 文件与普通日志不同:每次导出会覆盖之前的文件,而不是追加。
总结
- brpc 默认暴露全部 bvar,
/vars是统一的查询入口:支持精确名、多名称(;/,/空格分隔)与通配符(*多字符、$单字符); - 数值型 bvar 可点击查看 60 秒/60 分钟/24 小时/30 天共 174 个采样点构成的历史趋势,数据布局见 src/bvar/detail/series.h;
- 延迟类指标由
bvar::LatencyRecorder自动计算(平均、最大、QPS、count、50%/90%/99%/99.9%/99.99% 分位与 CDF),普通代码段也可用同一工具自行埋点; - 长尾(99% 之后)是 SLA 的最大威胁,CDF 越平、长尾面积越小越好;
- 只跑 client 或完全不用 brpc 的程序,可通过 dummy server 获得同样的可视化能力;生产环境还可借助
/brpc_metrics对接 Prometheus。
- RPC框架
- 后端
- 微服务
- 网络
- 通信
【免费下载链接】brpc
brpc 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".
相关推荐
brpc /vars 服务与 bvar 计数器实战:多线程指标统计、通配符查询与分位值分析指南
brpc /vars 服务与 bvar 计数器实战:多线程指标统计、通配符查询与分位值分析指南 本文以 brpc 内置的 /vars https://link.
后端RPC框架通信网络brpc /vars 监控服务完全指南:bvar 查询、历史趋势与分位数(CDF)分析实战
brpc /vars 监控服务完全指南:bvar 查询、历史趋势与分位数(CDF)分析实战 本文以 Apache brpc(工业级 C++ RPC 框架)的 d
后端RPC框架通信网络brpc 服务性能排查实战:基于内置 vars 指标与 rpcz/bvar 定位 CPU-bound 与 IO-bound 瓶颈
brpc 服务性能排查实战:基于内置 vars 指标与 rpcz/bvar 定位 CPU bound 与 IO bound 瓶颈 本篇技术指南以 brpc 官方
后端RPC框架通信网络
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考