brpc 内置服务Builtin Services完全指南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/brpcbrpc 通过 HTTP 协议向服务器暴露一组内置服务用于以多种形式展现服务器内部状态覆盖状态统计、指标计数、连接管理、配置修改、RPC 细节追踪与性能剖析等开发和调试场景。本文以官方文档 docs/cn/builtin_service.md 为主体结合仓库源码 src/brpc/builtin/ 中的真实实现系统讲解每个内置服务的功能、访问方式、关键参数与安全注意事项帮助读者在自建 brpc 服务上快速定位性能问题、排查故障并安全上线。什么是内置服务内置服务以多种形式展现服务器内部状态是 brpc 提高开发与调试效率的核心手段。brpc 通过HTTP 协议提供内置服务可通过浏览器或curl访问服务器会根据请求的User-Agent自动判断返回纯文本还是HTML你也可以在 URL 后添加?console1强制要求返回纯文本。例如在浏览器中打开http://server:port/status会得到带图表、可点击链接的 HTML 页面而在终端执行$ curl http://server:port/status则会得到纯文本输出方便脚本与命令行工具解析。当服务端口受限例如只开放了少数端口、笔记本无法直连时可以使用 rpc_view 进行转发访问。注意示例中的 logo 是百度内部的名称在开源版本中即为 brpc。安全模式对外隐藏内置服务内置服务包含了大量内部信息连接地址、gflags、进程内文件列表等出于安全考虑直接对外服务时需要隐藏内置服务包括经过 nginx 或其他 http server 转发流量的场景。具体的隐藏手段在 server.md 的安全模式章节 中有详细说明常用做法包括设置ServerOptions.internal_port为仅允许内网访问的端口通过内网端口访问内置服务对外端口访问时返回Not allowed to access builtin services错误通过 nginx 等反向代理将外部流量映射到具体的ServiceName/MethodName路径从根路径直接拒绝访问内置服务禁止在对外服务上开启-enable_dir_service与-enable_threads_service两个选项默认关闭它们虽方便但会严重泄露服务器信息需要完全禁用时可设置ServerOptions.has_builtin_services false。还可以用如下命令快速自检对外 RPC 服务是否误开了危险开关curl -s -m 1 HOSTNAME:PORT/flags/enable_dir_service,enable_threads_service | awk {if($3false){falsecnt}else if($3Value){isrpc1}}END{if(isrpc!1||falsecnt2){print SAFE}else{print NOT SAFE}}主要服务brpc 的核心内置服务主要分为七类覆盖了日常开发与线上运维的绝大部分需求。这些服务的实现代码位于仓库 src/brpc/builtin/ 目录例如status_service.cpp、vars_service.cpp、connections_service.cpp、flags_service.cpp、rpcz_service.cpp等入口页面由 index_service.cpp 统一渲染它根据是否 HTML 请求将首页重定向到/status或输出文本索引源码中通过UseHTML()判断并转发到StatusService::default_method见 index_service.cpp。/status服务主要状态总览/status 展示服务的主要统计信息。这些信息与/vars同源但按服务重新组织、便于查看。页面中各字段含义如下non_service_error在 service 处理过程之外的错误个数。当获取到合法的 service 后发生的错误算service_error否则如请求解析失败、service 名称不存在、请求并发度超限被拒绝等算non_service_error。服务过程中对后端服务的访问错误不属于它即使写出的 response 代表错误也记入对应 service。connection_count向该 server 发起请求的连接个数不包含记录在/vars/rpc_channel_connection_count的对外连接数。example.EchoService服务的完整名称包含 proto 中的包名。Echo (EchoRequest) returns (EchoResponse)方法签名一个服务可包含多个方法点击 request/response 上的链接可查看对应的 protobuf 结构体。count成功处理的请求总个数。error失败的请求总个数。latencyHTML 下从右到左分别是过去 60 秒、60 分钟、24 小时、30 天的平均延时纯文本下是 10 秒内由-bvar_dump_interval控制的平均延时。latency_percentiles延时的 80%、90%、99%、99.9% 分位值统计窗口默认 10 秒-bvar_dump_interval控制HTML 下有曲线。latency_cdf用 CDF累积分布函数展示分位值只能在 HTML 下查看。max_latencyHTML 下从右到左分别是过去 60 秒、60 分钟、24 小时、30 天的最大延时纯文本下是 10 秒内最大延时。qpsHTML 下从右到左分别是过去 60 秒、60 分钟、24 小时、30 天的平均 QPS纯文本下是 10 秒内平均 QPS。processing新版改名为concurrency正在处理的请求个数。压力归 0 后若此指标仍持续不为 0server 很可能有 bug例如忘记调用done或卡在某个处理步骤上。用户可以让对应 Service 实现brpc::Describable接口来自定义在 /status 页面上的描述接口定义见 src/brpc/describable.hclass MyService : public XXXService, public brpc::Describable { public: ... void Describe(std::ostream os, const brpc::DescribeOptions options) const { os my_status: blahblah; } };/vars可定制的指标计数器/vars 依赖 bvar —— 一套专为多线程环境设计的计数器类库支持单维度 bvar 和多维度 mbvar。bvar 利用 thread local 存储减少 cache bouncing相比竞争频繁的原子操作几乎不增加性能开销brpc 大量使用 bvar 提供统计数值。但 bvar 的本质是把写时的竞争转移到了读读时需合并所有写过的线程中的数据因此会变慢。当读写都很频繁、或需要基于最新值做逻辑判断时不应使用 bvar。查询方法/vars列出所有曝光的 bvar/vars/NAME查询名字为 NAME 的 bvar/vars/NAME1,NAME2,NAME3查询名字为 NAME1 或 NAME2 或 NAME3 的 bvar/vars/foo*,b$r查询名字与某一通配符匹配的 bvar注意用$代替?匹配单个字符因为?是 URL 的保留字符。/vars 左上角有一个搜索框可加快查找键入名称的一部分框架会自动补上*做模糊匹配不同名称间可用逗号、分号或空格分隔。命令行访问示例$ 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查看历史趋势点击大部分数值型 bvar 会显示历史趋势。每个可点击的 bvar 记录了过去 60 秒、60 分钟、24 小时、30 天总计 174 个值当有 1000 个可点击 bvar 时大约占用 1M 内存。分位值percentile的统计与查看x% 分位值是指把一段时间内的 N 个统计值排序后排在第N * x%位的值。例如 1000 个值排序后第 500 位是 50% 分位值中位数第 990 位是 99% 分位值第 999 位是 99.9% 分位值。分位值比平均值更能准确刻画数值分布对理解系统行为至关重要——工业级应用的 SLA 一般在 99.97% 以上一些系统即使平均值不错不佳的长尾区域也会明显拉低甚至打破 SLA。分位值可绘制为两种曲线CDF 曲线横轴是比例排序位置/总数纵轴是对应分位值。横轴50% 处对应的纵轴值即 50% 分位值。CDF 的导数是概率密度函数PDF但中位数密度很高在 PDF 中很醒目使长尾不易查看所以大部分系统选择 CDF 曲线。衡量 CDF 曲线好坏的两条简单规则越平越好水平线意味着所有数值相等没有等待、拥塞、停顿99% 与 100% 之间的面积越小越好99% 之后是长尾聚集地对 SLA 影响重大。按时间变化曲线包含 4 条曲线横轴是时间纵轴从上到下分别对应 99.9%、99%、90%、50% 分位值颜色从上到下越来越浅。该图不包含 99.99% 曲线因为 99.99% 分位值常明显大于 99.9% 及以下分位值画在一起会让其他曲线变得很矮可以点击以_latency_9999结尾的 bvar 独立查看。brpc 的服务都会自动统计延时分布无需用户自己添加。你也可以用bvar::LatencyRecorder统计任何代码的延时详细用法见 bvar-c#include bvar/bvar.h ... bvar::LatencyRecorder g_latency_recorder(client); // expose this recorder ... void foo() { ... g_latency_recorder my_latency; ... }如果程序使用了 brpc server即可在 /vars 看到client_latency、client_latency_cdf等变量并点击查看动态曲线。非 brpc server 场景如果程序只是 brpc client 或根本没有使用 brpc也想看到动态曲线可以参见 dummy_server。/connections所有连接的统计信息/connections 可以查看当前 server 的全部连接。一个典型页面包含三段信息第一段是server 接受accept的连接server_socket_count第二段是server 与下游的单连接使用brpc::Channel建立channel_socket_count其中 fd 为 -1 的是虚拟连接对应第三段中所有相同 RemoteSide 的连接第三段是server 与下游的短连接或连接池pooled connectionschannel_short_socket_count这些连接从属于第二段中相同 RemoteSide 的虚拟连接。表格各列含义列名含义RemoteSide远端的 IP 和端口SSL是否使用 SSL 加密为 Yes 时一般是 HTTPS 连接Protocol使用的协议可能为 baidu_std、hulu_pbrpc、sofa_pbrpc、memcache、http、public_pbrpc、nova_pbrpc、nshead_server 等fdfile descriptor文件描述符可能为 -1BytesIn/s上一秒读入的字节数In/s上一秒读入的消息数消息是对 request 和 response 的统称BytesOut/s上一秒写出的字节数Out/s上一秒写出的消息数BytesIn/m上一分钟读入的字节数In/m上一分钟读入的消息数BytesOut/m上一分钟写出的字节数Out/m上一分钟写出的消息数SocketId内部 id用于 debug用户不用关心该服务可帮助你识别出单连接、连接池与短连接三种下游连接形态对应截图single_conn.png、pooled_conn.png、short_conn.png可在 docs/images/ 目录找到并结合fd、BytesIn/s等列快速判断是否存在连接异常或流量倾斜。/flags所有 gflags 的状态与动态修改brpc 使用gflags管理配置/flags 可以查看服务器进程中所有的 gflags 并动态修改如果允许的话。相比传统configure方式gflags 的优势在于命令行和文件均可传入前者方便测试后者适合线上运维文件中的 gflags 可以 reload可在浏览器中查看并动态修改定义分散在与作用紧密关联的文件中更易管理。gflags 基本用法在需要它的源文件中#include gflags/gflags.h在全局 scope 用DEFINE_*type*(*name*, *default-value*, *description*);定义#include gflags/gflags.h ... DEFINE_bool(hex_log_id, false, Show log_id in hexadecimal); DEFINE_int32(health_check_interval, 3, seconds between consecutive health-checkings);在 main 函数开头用ParseCommandLineFlags处理程序参数#include gflags/gflags.h ... int main(int argc, char* argv[]) { google::ParseCommandLineFlags(argc, argv, true/*表示把识别的参数从argc/argv中删除*/); ... }要从文件加载 gflags加参数-flagfileconf/gflags.conf若希望默认就从文件读取可在程序中直接赋值google::SetCommandLineOption(flagfile, conf/gflags.conf);程序启动时会检查该文件是否存在不存在则报错conf/gflags.conf: No such file or directory。flagfile 语法要点来自 docs/cn/flags.md命令行中参数和值之间可不加等号flagfile 中必须加等号-param7或--param7否则不正确且不报错命令行中字符串可用引号包围flagfile 中不能加引号引号会成为值的一部分flagfile 中的值可以有空格如-namevalue with spacesflagfile 中参数可由单横线或双横线打头不能以三横线或更多横线打头否则是无效参数且不报错以#开头的行是注释开头的空格和空白行都会被忽略flagfile 中可以使用--flagfile包含另一个 flagfile。动态修改 gflagon-the-fly/flags列出所有 gflags/flags/NAME查询名字为 NAME 的 gflag/flags/NAME1,NAME2,NAME3查询多个 gflag/flags/foo*,b$r通配符查询$代替?访问/flags/NAME?setvalueVALUE即可动态修改validator 会被调用。修改过的 flags 会以红色高亮修改过指修改这一行为即使再改回默认值仍显示红色。为了防止误修改需要动态修改的 gflag必须有 validator显示此类 gflag 名字时有(R)后缀。修改成功、尝试修改不允许修改的 gflag、设置不允许的值flag 值不会变化都会显示对应提示信息。关于重载 gflags重点关注三点原文重点务必遵守避免在一段代码中多次读取同一个 gflag应把值保存下来再使用因为 gflag 的值随时可能变化使用google::GetCommandLineOption()访问 string 类型的 gflag直接访问是线程不安全的处理逻辑和副作用应放到 validator 里。比如修改FLAGS_foo后需更新另一处的值如果写在程序初始化处而不是 validator 里重载时这段逻辑就运行不到。如果确认某个 gflag 不需要额外的线程同步和处理逻辑即可重载可为其注册一个总是返回 true 的 validatorDEFINE_bool(hex_log_id, false, Show log_id in hexadecimal); BRPC_VALIDATE_GFLAG(hex_log_id, brpc::PassValidate/*always true*/);对 int32 和 int64 类型有判断是否为正数的常用 validatorDEFINE_int32(health_check_interval, 3, seconds between consecutive health-checkings); BRPC_VALIDATE_GFLAG(health_check_interval, brpc::PositiveInteger);以上操作都可以在命令行中完成例如$ curl brpc.baidu.com:8765/flags/health_check_interval Name | Value | Description | Defined At --------------------------------------- health_check_interval (R) | 3 | seconds between consecutive health-checkings | src/brpc/socket_map.cpp另外gflags 还提供了-immutable_flags开关打开后所有 gflags 将不能被动态修改。当一个服务对某个 gflag 值比较敏感、不希望线上被误改时可打开此开关但打开的同时也意味着无法动态修改线上配置每次修改都要重启程序因此对于还在调试或待收敛阶段的程序不建议打开。/rpcz查看所有 RPC 的细节/rpcz 用于查看最近请求的详细信息并可插入注释annotation。与 tracing 系统以全局视角看整体系统延时分布不同rpcz 更多是一个调试工具不过在 brpc 中 rpcz 与 tracing 的数据来源是一样的。采样机制当每秒请求数小于 1 万时rpcz 记录所有请求超过 1 万时rpcz 会随机忽略一些请求把采样数控制在 1 万左右。rpcz 可以淘汰时间窗口之前的数据通过-span_keeping_seconds选项设置默认 1 小时。关于开销实现完全规避了线程竞争开销极小即使采集了几千万条请求内存占用一般在 50 兆以内rpcz 会占用一些磁盘空间就像日志一样若设定存一小时数据一般在几百兆左右。开关方法默认不开启加入-enable_rpcz选项会在启动后开启。相关 gflags 如下NameValueDescriptionDefined Atenable_rpcz (R)true (default:false)Turn on rpczsrc/brpc/builtin/rpcz_service.cpprpcz_hex_log_id (R)falseShow log_id in hexadecimalsrc/brpc/builtin/rpcz_service.cpprpcz_database_dir./rpc_data/rpczFor storing requests/contexts collected by rpczsrc/brpc/span.cpprpcz_keep_span_dbfalseDont remove DB of rpcz at programs exitsrc/brpc/span.cpprpcz_keep_span_seconds (R)3600Keep spans for at most so many secondssrc/brpc/span.cpprpcz_save_span_min_latency_us (R)0 (default:0)The minimum latency microseconds of span savedsrc/brpc/span.cpp若启动时未加-enable_rpcz可在启动后访问SERVER_URL/rpcz/enable动态开启访问SERVER_URL/rpcz/disable关闭——这两个链接等价于访问/flags/enable_rpcz?setvaluetrue和/flags/enable_rpcz?setvaluefalse。较新版本在 html 页面中增加了按钮可视化地开启/关闭。如果只是 brpc client 或没有使用 brpc参见 dummy_server。数据展现/rpcz 展现的数据分为两层。第一层是最近请求的概况点击链接进入第二层第二层展示某系列trace或某个请求span的详细信息也可把trace和span作为 query-string 拼出链接直达。内容说明时间分为绝对时间精确到微秒和前一个时间的差值traceID有点像session id对应一次对外服务牵涉到的所有服务上下游 server 共用一个 trace-idspanID对应一个 server 或 client 中一个请求的处理过程trace-id 和 span-id 在概率上唯一第一层页面中的request和response后是数据包字节数包括附件但不包括协议 meta第二层中字节数一般在括号里如Responded(13)中的 13点击链接可能访问其他 server 上的 rpcz点浏览器后退一般会返回到之前位置Im the last call, Im about to ...等都是用户的 annotation。Annotation只要使用了 brpc就可以用TRACEPRINTF定义见 src/brpc/traceprintf.h打印内容到事件流TRACEPRINTF(Hello rpcz %d, 123);这条 annotation 会按其发生时间插入到对应请求的 rpcz 中。从这个角度看rpcz 是请求级的日志。用 TRACEPRINTF 打印沿途上下文可以看到请求在每个阶段停留的时间、牵涉到的数据集和参数。跨 bthread 传递 trace 上下文有的业务在 server 请求处理中会创建子 bthread 并发起 rpc默认情况下子 bthread 中的 rpc 与原来请求无法建立关联trace 会断掉。此时可在创建子 bthread 时指定BTHREAD_INHERIT_SPAN标志显式建立 trace 上下文关联bthread_attr_t attr { BTHREAD_STACKTYPE_NORMAL, BTHREAD_INHERIT_SPAN, nullptr }; bthread_start_urgent(tid, attr, thread_proc, arg);Span 生命周期管理brpc 使用智能指针std::shared_ptr/std::weak_ptr管理 Span 对象生命周期并通过自旋锁保护并发访问解决了三类问题父 Span 通过shared_ptr持有子 Span 强引用、TLS 中用weak_ptr存储防止 use-after-free即使 server 在子 bthread 完成前返回 response也不会访问到已释放的 Span使用自旋锁保护_client_list与_info的并发修改支持多个 bthread 同时创建子 span 或添加 annotation父 Span 销毁时通过_client_list.clear()自动清理所有子 Span无需手动管理。因此使用BTHREAD_INHERIT_SPAN时无需担心 Span 生命周期问题可安全用于异步场景。profiler 服务性能剖析三件套cpu profiler分析 CPU 热点heap profiler分析内存占用contention profiler分析锁竞争。三者分别对应pprof_service.cpp、memory_service.cpp、hotspots_service.cpp等内置服务实现见 src/brpc/builtin/ 目录通过/pprof等 HTTP 端点触发采样配合 pprof 工具可生成调用火焰图与文本报告是定位 CPU 热点、内存泄漏与锁竞争问题的标准手段。其他服务/version查看服务器版本/version 查看服务器的版本。用户可通过Server::set_version()设置 Server 的版本如果用户没有设置框架会自动生成规则为brpc_server_service-name1_service-name2 ...虽然名字叫version但设置的值请包含服务名而不只是一个数字版本参见 server.md 的版本设置。对应实现见 src/brpc/builtin/version_service.cpp。/health探测服务存活情况/health 用于探测服务的存活情况默认返回 OK。对应实现见 src/brpc/builtin/health_service.cpp负载均衡与监控系统可定期轮询该端点判断服务是否存活。如需定制 /health 页面的内容可继承brpc::HealthReporter定义见 src/brpc/health_reporter.h在其中实现生成页面的逻辑并把实例赋给ServerOptions.health_reporter该实例不被 server 拥有必须保证在 server 运行期间有效可在定制逻辑中根据业务运行状态返回更多样的状态信息。/protobufs查看所有 protobuf 结构体/protobufs 查看程序中所有的 protobuf 结构体方便确认服务加载了哪些 message 定义及其字段布局。对应实现见 src/brpc/builtin/protobufs_service.cpp。/vlog查看当前可开启的 VLOG/vlog 查看程序中当前可开启的 VLOG对 glog 无效。对应实现见 src/brpc/builtin/vlog_service.cpp。/dir浏览服务器文件默认关闭/dir可以浏览服务器上的所有文件方便但非常危险默认关闭。它受-enable_dir_service控制对应实现见 src/brpc/builtin/dir_service.cpp。在对外服务上开启它意味着任何人都能遍历服务器文件系统因此除非是可信内网环境否则不要开启。/threads查看所有线程运行状况默认关闭/threads查看进程内所有线程的运行状况调用时对程序性能影响较大默认关闭。它受-enable_threads_service控制对应实现见 src/brpc/builtin/threads_service.cpp。该服务可用于排查死锁、线程卡死等问题但注意其在采集时会阻塞线程不要在流量高峰随意调用。源码视角内置服务的统一实现机制从源码结构看src/brpc/builtin/ 目录所有内置服务本质上都是普通的 brpc HTTP 服务每个服务对应一个*_service.cpp/.h文件例如status_service、vars_service、connections_service、flags_service、rpcz_service、version_service、health_service、protobufs_service、vlog_service、dir_service、threads_service等另有index_service负责首页索引、list_service负责列出服务、common.cpp/h提供 HTML 渲染与 tab 导航等公共逻辑、get_favicon_service/get_js_service提供页面静态资源。在 index_service.cpp 的default_method中可以看到内置服务统一的响应逻辑先通过UseHTML()依据请求判断返回 HTML 还是纯文本HTML 请求且未指定as_more时直接转发到StatusService::default_method渲染首页状态否则输出带 tab 导航的完整服务列表。这解释了为什么浏览器访问与 curl 访问同一 URL 会得到不同格式——由 User-Agent 驱动的UseHTML()判断贯穿所有内置服务。小结内置服务的正确打开方式brpc 内置服务是开箱即用的运维与调试工具建议按如下方式使用开发调试阶段用浏览器或curl http://host:port/status?console1快速查看服务状态用/vars查询指标、/rpcz追踪请求细节、三个 profiler 定位性能瓶颈上线之前严格按照安全模式的要求隐藏内置服务internal_port、nginx 转发、has_builtin_servicesfalse确认未开启enable_dir_service与enable_threads_service必要时开启-immutable_flags防止线上误改配置线上运维通过/flags动态调整参数需 validator 支持、通过/connections观察连接形态、通过/health配合负载均衡探活。掌握这套内置服务等于拥有了一整套面向 brpc 服务的仪表盘 调试器能够显著提升从开发到上线的效率与安全性。【免费下载链接】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),仅供参考