尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

brpc Backup Request 备份请求机制完全指南:双路竞速、参数调优与限流策略

brpc Backup Request 备份请求机制完全指南:双路竞速、参数调优与限流策略 brpc Backup Request 备份请求机制完全指南双路竞速、参数调优与限流策略【免费下载链接】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 的 Backup Request备份请求机制用于在多路服务同时访问、哪个先返回取哪个的高可用场景下以极低的额外开销换取延迟毛刺的消除。本文基于 docs/cn/backup_request.md 展开结合仓库源码与 example/backup_request_c 示例完整讲解 Channel 级 backup request 的触发原理、backup_request_ms的调参方法、限流策略内置与自定义以及跨集群互备的 SelectiveChannel 方案读完即可在真实项目中落地这一能力。为什么需要 Backup Request双路竞速先到先得在某些高可用场景中调用方需要同时访问两路服务哪一路先返回就取哪一路的结果。最朴素的做法是同时发出两个请求但这样后端服务会承受两倍压力。brpc 提供的 Backup Request 机制把同时发优化为必要时才补发Channel 先向其中一个 server 发送请求如果在ChannelOptions.backup_request_ms指定的毫秒数之后请求仍未返回再向另一个 server 发送同样的请求之后哪一路先返回就取哪一路。在设置了合理backup_request_ms的前提下大部分时候只会发出一个请求对后端服务只有一倍压力仅在慢请求触发补发时才出现短暂的双路并发。这正是该机制的核心价值用偶尔的两倍压力换取稳定的低延迟。场景一后端 server 可以挂在一个命名服务内这是最常见的部署形态——所有后端 server 都注册在同一个命名服务Naming Service中由 Channel 的负载均衡器统一调度。此时只需在 ChannelOptions 上开启 backup request 即可。官方示例backup_request_c仓库中的 example/backup_request_c 演示了完整流程。client 端设置了backup_request_ms 2msserver 端在收到偶数编号的请求后会故意睡眠 20ms 以触发 backup request。client 端核心配置example/backup_request_c/client.cppDEFINE_int32(backup_request_ms, 2, Timeout for sending backup request); brpc::ChannelOptions options; options.protocol FLAGS_protocol; options.connection_type FLAGS_connection_type; options.timeout_ms FLAGS_timeout_ms/*milliseconds*/; options.max_retry FLAGS_max_retry; options.backup_request_ms FLAGS_backup_request_ms; if (channel.Init(FLAGS_server.c_str(), FLAGS_load_balancer.c_str(), options) ! 0) { LOG(ERROR) Fail to initialize channel; return -1; }server 端故意制造慢请求example/backup_request_c/server.cpp// Sleep a while for 0th, 2nd, 4th, 6th ... requests to trigger backup request // at client-side. bool do_sleep (_count.fetch_add(1, butil::memory_order_relaxed) % 2 0); if (do_sleep) { LOG(INFO) , sleep FLAGS_sleep_ms ms to trigger backup request noflush; } ... if (do_sleep) { bthread_usleep(FLAGS_sleep_ms * 1000); }运行后client 端和 server 端日志中的 index 是请求编号server 在收到第一个请求后故意 sleep 20msclient 随即发出另一个同样 index 的请求而最终 RPC 延时并未受到这次故意 sleep 的影响brpc 自带的 /rpcz 页面同样能观察到client 在 2ms 后触发了 backup 超时并发出第二个请求源码级原理一次 RPC 中的两个定时器从源码看backup request 并非独立机制而是叠加在普通 RPC 重试框架之上的一次特殊调度。在 src/brpc/channel.cpp 中Channel 在每次 RPC 发起时会把未显式设置的 Controller 参数用 ChannelOptions 补齐if (cntl-backup_request_ms() UNSET_MAGIC_NUM nullptr cntl-_backup_request_policy) { cntl-set_backup_request_ms(_options.backup_request_ms); cntl-_backup_request_policy _options.backup_request_policy; }随后进入定时器注册逻辑src/brpc/channel.cpp这里有两条关键分支若backup_request_ms() 0且backup_request_ms() timeout_ms()或timeout_ms() 0表示不限时则注册一个backup 定时器在backup_request_ms * 1000 start_send_real_us时刻触发HandleBackupRequest回调同时_deadline_us被设置为整体超时时刻用于在EBACKUPREQUEST发生时截断_connect_timeout_ms并重置定时器否则backup 未开启或已到整体超时注册普通的HandleTimeout超时定时器。也就是说backup_request_ms 必须小于 timeout_ms 才会生效除非 timeout_ms 0 表示永不超时。补发的 backup 请求本身同样受整体超时约束并不会无限等待。src/brpc/channel.h 中的注释也明确说明了这一点且Controller.set_backup_request_ms()可以在单次 RPC 粒度覆盖 Channel 级配置。限制流式请求不适用 backup值得注意的是当 RPC 携带 request stream请求流时无法正确处理重试与 backupsrc/brpc/channel.cpp 会强制关闭if (!cntl-_request_streams.empty()) { // Currently we cannot handle retry and backup request correctly cntl-set_max_retry(0); cntl-set_backup_request_ms(-1); cntl-_backup_request_policy nullptr; }流式场景请勿依赖 backup request 来保障可用性。选择合理的 backup_request_msbackup_request_ms太小会导致 backup 请求频繁发出对后端压力上升太大则失去消除延迟毛刺的意义因此需要结合真实延时分布来确定。借助 latency_cdf 图确定阈值brpc 默认提供 latency_cdf累计分布函数图也可自行添加。cdf 图的 y 轴是延时默认微秒x 轴是延时小于 y 轴取值的请求比例。在下图中选择backup_request_ms 2ms可以大约覆盖 95.5% 的请求选择backup_request_ms 10ms则可以覆盖 99.99% 的请求。一般建议选取能覆盖 95%~99% 请求的延时作为backup_request_ms绝大多数请求无需补发仅针对长尾慢请求触发 backup兼顾压力与延迟。自行添加 latency 记录器若需要针对自定义函数测量延时分布可以使用 bvar 的LatencyRecorderdocs/cn/backup_request.md 给出的标准做法#include bvar/bvar.h #include butil/time.h ... bvar::LatencyRecorder my_func_latency(my_func); ... butil::Timer tm; tm.start(); my_func(); tm.stop(); my_func_latency tm.u_elapsed(); // u代表微秒还有s_elapsed(), m_elapsed(), n_elapsed()分别对应秒毫秒纳秒。 // 好了在/vars中会显示my_func_qps, my_func_latency, my_func_latency_cdf等很多计数器。LatencyRecorder会自动派生出 QPS、平均/分位延迟、cdf 等一系列计数器/vars页面可直接查看。Backup Request 限流防止备份风暴开启 backup request 后如果后端出现大面积抖动补发请求可能成倍放大对后端的压力。为此 brpc 提供了按比例限流能力当 backup 请求占全部请求的比例超过上限时暂时停止补发。优先级顺序为backup_request_policybackup_request_ms。即一旦设置了 policyChannelOptions.backup_request_ms的数值将被 policy 中返回的超时阈值取代policy 返回 -1 时才回退到 channel 级配置。使用内置限流策略调用工厂函数CreateRateLimitedBackupPolicy创建限流策略并设置到ChannelOptions.backup_request_policy完整接口见 src/brpc/backup_request_policy.h#include brpc/backup_request_policy.h #include memory brpc::RateLimitedBackupPolicyOptions opts; opts.backup_request_ms 10; // 超过10ms未返回时发送backup请求 opts.max_backup_ratio 0.3; // backup请求比例上限30% opts.window_size_seconds 10; // 滑动窗口宽度秒 opts.update_interval_seconds 5; // 缓存比例的刷新间隔秒 // CreateRateLimitedBackupPolicy返回的指针由调用方负责释放。 // policy的生命周期必须长于channel——先销毁channel再销毁policy。 std::unique_ptrbrpc::BackupRequestPolicy policy( brpc::CreateRateLimitedBackupPolicy(opts)); brpc::ChannelOptions options; options.backup_request_policy policy.get(); // Channel不拥有该对象 channel.Init(..., options); // channel必须在policy析构之前销毁。RateLimitedBackupPolicyOptions参数说明默认值与校验逻辑与 src/brpc/backup_request_policy.cpp 中的实现一一对应字段默认值说明backup_request_ms-1超时阈值毫秒。-1 表示继承ChannelOptions.backup_request_ms仅在通过ChannelOptions.backup_request_policy设置策略时有效通过 Controller 注入时没有 channel 级的回退值应显式指定 0 的值。必须 -1。max_backup_ratio0.1backup比例上限取值范围 (0, 1]window_size_seconds10滑动窗口宽度秒取值范围 [1, 3600]update_interval_seconds5缓存刷新间隔秒必须 1参数不合法时CreateRateLimitedBackupPolicy返回nullptr。另外源码中还有一个额外提醒若update_interval_seconds大于window_size_seconds窗口在自身周期内几乎不会刷新比例会打印 WARNING配置时应让刷新间隔不大于窗口宽度。使用自定义 BackupRequestPolicy如需完全控制决策逻辑可实现BackupRequestPolicy接口定义见 src/brpc/backup_request_policy.h并设置到ChannelOptions.backup_request_policy#include brpc/backup_request_policy.h class MyBackupPolicy : public brpc::BackupRequestPolicy { public: int32_t GetBackupRequestMs(const brpc::Controller*) const override { return 10; // 10ms后发送backup } bool DoBackup(const brpc::Controller*) const override { return should_allow_backup(); // 自定义逻辑 } void OnRPCEnd(const brpc::Controller*) override { // 每次RPC结束时调用可在此更新统计 } }; MyBackupPolicy my_policy; brpc::ChannelOptions options; options.backup_request_policy my_policy; // Channel不拥有该对象需保证其生命周期长于Channel channel.Init(..., options);三个接口方法各司其职GetBackupRequestMs返回补发 backup 的超时毫秒数。返回 -1 表示继承ChannelOptions.backup_request_ms返回其他负值表示对该 RPC 禁用 backupsrc/brpc/backup_request_policy.hDoBackup返回 true 则发送 backup 请求是限流决策的入口OnRPCEnd每次 RPC 结束时调用每个用户 RPC 调用一次而非每个请求分支用于收集调用信息以调整策略。实现说明近似的尽力而为限流从 src/brpc/backup_request_policy.cpp 的BackupRateLimiter实现可以看清几个关键设计滑动窗口统计backup 数与总请求数分别通过bvar::Window在滑动时间窗口内统计窗口无采样数据时冷启动前几秒回退到累计计数避免比例失真缓存比例 无锁 CAS 刷新缓存的比例值最多每update_interval_seconds刷新一次刷新动作由compare_exchange_strong无锁竞争产生因此每次 RPC 的公共路径开销极低——仅有两次原子读一次读缓存比例、一次读上次刷新时间决策即时计数backup 决策在做出时立即计数ShouldAllow()中_backup_count 1而总 RPC 数在完成时OnRPCEnd统计。这意味着在延迟抖动期间比例可能短暂滞后backup 已发出但对应 RPC 尚未完成比例暂时偏低。这是设计有意为之——限流器追求的是近似的尽力而为节流而非精确执行源码中还特别处理了窗口内有 backup 但无完成 RPC的极端情况返回保守的 ratio1.0防止 backup 风暴每个使用限流的 Channel 会维护两个bvar::Window采样任务在 Channel 数量极多的部署中需要留意这部分后台开销。场景二后端 server 不能挂在一个命名服务内当两路或多路服务无法注册进同一个命名服务时典型如两个独立集群互备有两条实现路径。【推荐】SelectiveChannel集群间互备建立一个开启 backup request 的 SelectiveChannel其中包含两个 sub channel。访问这个 SelectiveChannel 与场景一的流程类似先访问一个 sub channel如果在ChannelOptions.backup_request_ms后没有返回再访问另一个 sub channel。如果一个 sub channel 对应一个集群这个方法就是在两个集群之间做互备对任意一个集群都只产生必要时才补发的压力。SelectiveChannel 的完整示例见 example/selective_echo_c具体做法与场景一相同——在 SelectiveChannel 的ChannelOptions中设置backup_request_ms或backup_request_policy即可backup 请求会自动落到另一个 sub channel 上。【不推荐】双异步 RPC Join 取消另一种做法是发起两个异步 RPC 后 Join 它们并在两个 done 回调内做相互取消逻辑示例见 example/cancel_c。这种方法的问题在于总会发出两个请求对后端服务造成两倍压力无论第一个请求多快返回都无法避免第二个请求已经发出的事实。从资源利用角度看它怎么算都是不经济的应尽量避免使用除非有 backup request 无法覆盖的特殊需求。最佳实践小结能用命名服务就用 Channel 内置 backup request只需设置backup_request_ms成本最低、效果最好用 latency_cdf 定参取能覆盖 95%~99% 请求的延时作为backup_request_ms并在上线后持续观察/vars与/rpcz验证实际补发比例高并发场景务必开启限流通过CreateRateLimitedBackupPolicy设定max_backup_ratio默认 0.1防止后端抖动时 backup 风暴放大压力追求极致控制时可实现自定义BackupRequestPolicy注意生命周期policy 由调用方持有必须保证其生命周期长于 Channel先销毁 Channel再销毁 policy跨集群互备用 SelectiveChannel让每个 sub channel 对应一个集群backup 请求天然落到另一个集群实现低成本双集群容灾避开不经济方案双异步 RPC Join 总是发两倍请求非必要不用。通过以上配置你可以在 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),仅供参考
返回列表