十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于请求超时时间的限流实战:brpc Timeout Concurrency Limiter 原理与配置全解

基于请求超时时间的限流实战:brpc Timeout Concurrency Limiter 原理与配置全解 基于请求超时时间的限流实战brpc Timeout Concurrency Limiter 原理与配置全解【免费下载链接】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本篇技术指南聚焦 brpc 中基于请求超时时间的自适应限流方案Timeout Concurrency Limiter讲解它在服务过载场景下的核心算法、method 级开启方法、完整配置参数与源码级实现原理。读完本文你将掌握如何通过一行配置让某个 RPC method 根据平均延迟 vs 请求超时时间动态决定接受或拒绝请求从而在过载时保住服务整体可用性。为什么需要超时感知的限流服务的处理能力是有客观上限的。当请求速度超过服务的处理速度时服务就会过载overload。如果服务持续过载会导致越来越多的请求积压最终所有请求都必须等待较长时间才能被处理从而使整个服务处于瘫痪状态。与之相对的如果直接拒绝掉一部分请求反而能够让服务能够及时处理更多的请求。这正是限流的核心价值用局部的失败换取整体的存活。brpc 中常规的做法是设置最大并发max_concurrency当并发达到上限时直接以ELIMIT错误拒绝新请求。但固定并发数存在一个天然短板请求延迟是动态变化的。流量的增减、请求体的大小变化、磁盘的顺序/随机读写这些因素都会引起请求延迟波动。一个拍脑袋定死的并发上限要么在延迟升高时仍放行过多请求导致雪崩要么在延迟平稳时白白浪费处理能力。Timeout Concurrency Limiter 正是为解决并发上限应当随服务实时状态自适应这一问题而设计的。核心算法用平均延迟与请求超时时间做准入决策在服务正常运营过程中用户一般情况下不希望请求延迟的波动造成错误即使会有一些请求的排队造成请求延迟增加。因此一般用户设置的请求超时时间都会是服务平均延迟的 3 至 4 倍——这是一个比较宽裕的安全余量。基于请求超时时间的限流其思想正是利用这个余量服务端持续统计服务的平均延迟_avg_latency_us在请求到达时将该平均延迟与当前请求设置的超时时间相比较估算该请求是否能够在设置的超时时间内完成处理如果能完成则接受请求如果不能完成则拒绝请求。由于统计得到的服务平均延迟和当前请求的实际延迟之间会有一定的时间差延迟统计是滞后的因此还需要设置一个比较宽泛的最大并发度max_concurrency作为兜底保证服务不会因为突然的慢请求造成短时间内堆积过多请求。用伪代码可以描述为接收请求 当且仅当: 当前并发数 最大并发兜底值 且 统计平均延迟 当前请求超时时间这个决策逻辑在源码 src/brpc/policy/timeout_concurrency_limiter.cpp 的OnRequested中有着严格对应bool TimeoutConcurrencyLimiter::OnRequested(int current_concurrency, Controller *cntl) { auto timeout_ms _timeout_ms; if (cntl ! nullptr cntl-timeout_ms() ! UNSET_MAGIC_NUM) { timeout_ms cntl-timeout_ms(); } // In extreme cases, the average latency may be greater than requested // timeout, allow currency_concurrency is 1 ensures the average latency can // be obtained renew. return current_concurrency 1 || (current_concurrency _max_concurrency _avg_latency_us timeout_ms * 1000); }两点值得注意请求超时时间优先取自请求本身如果Controller中携带了有效超时cntl-timeout_ms()不是UNSET_MAGIC_NUM则用请求级超时否则回退到限流器自身的_timeout_ms来自TimeoutConcurrencyConf或默认 FLAGS。current_concurrency 1的保底放行注释明确解释了原因——在极端情况下平均延迟可能已经大于请求超时时间此时必须允许并发为 1 的请求进入以保证平均延迟统计能够持续被刷新更新否则限流器会锁死在拒绝所有请求的状态永远无法恢复。开启方法method 级配置实战目前只有 method 级别支持基于超时的限流。要为某个 method 开启基于超时的限流只需要将它的最大并发设置为字符串timeout即可。方式一字符串配置使用全局默认参数如果客户端没有开启FLAGS_baidu_std_protocol_deliver_timeout_ms即请求超时时间没有随协议送达服务端可以设置FLAGS_timeout_cl_default_timeout_ms来调整一个默认的请求超时时间同时可以设置FLAGS_timeout_cl_max_concurrency来调整兜底的最大并发度。方式二TimeoutConcurrencyConf 按 method 定制也可以通过设置brpc::TimeoutConcurrencyConf为每个 method 指定不同的配置。TimeoutConcurrencyConf定义在 src/brpc/adaptive_max_concurrency.h只有两个字段// timeout concurrency limiter config struct TimeoutConcurrencyConf { int64_t timeout_ms; // 用于比较的请求超时时间毫秒 int max_concurrency; // 兜底最大并发度 };完整代码示例下面这段示例完整继承自原文档并加以场景标注。既可以对全部 method 生效也可以精确到单个 method// Set timeout concurrency limiter for all methods brpc::ServerOptions options; // 方式一字符串 timeouttimeout_ms 与 max_concurrency 取 FLAGS 默认值 options.method_max_concurrency timeout; // 方式二显式指定 {timeout_ms, max_concurrency}例如超时 1ms、兜底并发 100 options.method_max_concurrency brpc::TimeoutConcurrencyConf{1, 100}; // Set timeout concurrency limiter for specific method // 方式一仅针对 example.EchoService.Echo 开启 server.MaxConcurrencyOf(example.EchoService.Echo) timeout; // 方式二仅针对该 method 指定定制参数 server.MaxConcurrencyOf(example.EchoService.Echo) brpc::TimeoutConcurrencyConf{1, 100};字符串 timeout 是如何被识别的options.method_max_concurrency的类型是AdaptiveMaxConcurrency见 src/brpc/server.h 中的ServerOptions::method_max_concurrency可被Server.MaxConcurrencyOf()覆盖。从源码 src/brpc/adaptive_max_concurrency.cpp 可以看到它的解析逻辑AdaptiveMaxConcurrency::AdaptiveMaxConcurrency(const butil::StringPiece value) : _max_concurrency(0) { int max_concurrency 0; if (butil::StringToInt(value, max_concurrency)) { operator(max_concurrency); // 能解析为整数 - 固定并发 } else { value.CopyToString(_value); // 否则存为字符串类型如 timeout _max_concurrency -1; // 标记为 user-defined 类型 } }即赋值一个数字字符串表示固定最大并发赋值一个非数字字符串如timeout则表示用户自定义类型的限流器。而TimeoutConcurrencyConf的赋值重载会把_value直接置为timeout、_max_concurrency置为-1见 src/brpc/adaptive_max_concurrency.cpp。在服务初始化时brpc 通过扩展点注册表把timeout名称绑定到TimeoutConcurrencyLimiter实现见 src/brpc/global.cpp 的ConcurrencyLimiterExtension()-RegisterOrDie(timeout, ...)并调用New()以TimeoutConcurrencyConf构造真正的限流器实例见 src/brpc/policy/timeout_concurrency_limiter.cpp。配置参数详解除了文档中直接提到的两个 FLAGSTimeoutConcurrencyLimiter实际还暴露了多个采样与惩罚相关的参数全部定义于 src/brpc/policy/timeout_concurrency_limiter.cpp汇总如下参数名默认值说明timeout_cl_default_timeout_ms500RPC 请求的默认超时时间毫秒。客户端未随协议送达超时时使用timeout_cl_max_concurrency100当平均延迟统计尚未刷新时限制请求不超过该最大并发度兜底值timeout_cl_sample_window_size_ms1000采样窗口时长毫秒timeout_cl_min_sample_count100一个采样窗口内采集的请求数若小于该值整个窗口作废丢弃timeout_cl_max_sample_count200一个采样窗口内采集的请求数一旦超过该值即使窗口时长未到也立即结算并开启新窗口timeout_cl_sampling_interval_ms0.1采样间隔毫秒控制请求采样的频率timeout_cl_initial_avg_latency_us500平均延迟的初始值微秒在统计未建立前参与准入判断timeout_cl_enable_error_punishtrue计算最大并发时是否考虑失败请求timeout_cl_fail_punish_ratio1.0用失败请求惩罚正常请求的比例系数该值越大惩罚越激进其中timeout_cl_default_timeout_ms与timeout_cl_max_concurrency正是原文档中提到的两个 FLAGS前者决定默认请求超时时间后者决定最大并发度兜底值。二者同时也在TimeoutConcurrencyLimiter的默认构造函数中被引用为初始值见 src/brpc/policy/timeout_concurrency_limiter.cpp而当通过TimeoutConcurrencyConf构造时则使用配置中显式指定的timeout_ms与max_concurrency同文件 L61-L66。源码级原理剖析平均延迟是如何统计出来的准入决策依赖两个量当前并发数由框架维护和_avg_latency_us。后者正是本限流器自学习更新的核心状态其更新链路位于 src/brpc/policy/timeout_concurrency_limiter.cpp 的OnResponded中每当一个请求响应完成错误码为ELIMIT的除外即被限流拒绝的请求不参与统计在满足采样间隔的前提下调用AddSample把本次请求的error_code与latency_us记入当前采样窗口。采样窗口SampleWindow采样窗口定义在头文件 src/brpc/policy/timeout_concurrency_limiter.h是一个简单的计数结构struct SampleWindow { int64_t start_time_us; // 窗口起始时间 int32_t succ_count; // 成功请求数 int32_t failed_count; // 失败请求数 int64_t total_failed_us; // 失败请求累计延迟 int64_t total_succ_us; // 成功请求累计延迟 };窗口结算的完整规则在AddSamplesrc/brpc/policy/timeout_concurrency_limiter.cpp中若窗口内成功失败请求数不足timeout_cl_min_sample_count且窗口时长已到timeout_cl_sample_window_size_ms则整个采样窗口作废丢弃ResetSampleWindow不更新平均延迟若请求数达到timeout_cl_min_sample_count且窗口时长已到或请求数达到timeout_cl_max_sample_count则立即结算窗口结算时若有成功请求调用UpdateAvgLatency()更新平均延迟若全部请求都失败则将平均延迟直接翻倍_avg_latency_us * 2这是为了让限流快速收紧、避免继续放行结算后重置窗口开始新一轮统计。平均延迟公式与失败惩罚UpdateAvgLatencysrc/brpc/policy/timeout_concurrency_limiter.cpp是核心公式void TimeoutConcurrencyLimiter::UpdateAvgLatency() { double failed_punish _sw.total_failed_us * FLAGS_timeout_cl_fail_punish_ratio; auto avg_latency_us std::ceil((failed_punish _sw.total_succ_us) / _sw.succ_count); AdjustAvgLatency(avg_latency_us); }可以看到失败请求的累计延迟会乘以timeout_cl_fail_punish_ratio默认 1.0后叠加进分子但分母只按成功请求数succ_count计算。换言之失败请求越多、延迟越高统计出的平均延迟就会被抬得越高从而让准入条件_avg_latency_us timeout_ms * 1000更快不成立、更早拒绝新请求——这就是失败惩罚的机制。该机制可通过timeout_cl_enable_error_punish关闭通过增大timeout_cl_fail_punish_ratio让惩罚更激进。另外注意OnResponded会跳过ELIMIT错误码的请求src/brpc/policy/timeout_concurrency_limiter.cpp被限流拒绝的请求根本不进入服务处理其延迟不具备参考意义不应污染统计。测试验证仓库提供了专门的单元测试 test/brpc_timeout_concurrency_limiter_unittest.cpp 来验证上述行为例如AddSample用例验证采样窗口在样本数不足min_sample_count时整窗作废、延迟统计不更新样本达到max_sample_count时提前结算成功/失败计数succ_count/failed_count正确累计OnResponded用例验证成功与失败请求进入采样统计的路径AdaptiveMaxConcurrencyTest用例验证AdaptiveMaxConcurrency无论是通过TimeoutConcurrencyConf{100, 100}构造、还是通过字符串timeout赋值其type()与value()均为timeout且TimeoutConcurrencyConf的timeout_ms/max_concurrency字段能够被完整保真地取回——这印证了字符串配置与结构体配置两种方式殊途同归。适用前提与注意事项结合原文档与源码使用该限流器时有几点需要明确仅 method 级生效目前只有 method 级别支持基于超时的限流请通过ServerOptions::method_max_concurrency或Server.MaxConcurrencyOf()配置超时来源若客户端未开启FLAGS_baidu_std_protocol_deliver_timeout_ms将请求超时随协议送达服务端将使用timeout_cl_default_timeout_ms或TimeoutConcurrencyConf::timeout_ms作为比较基准请确保其与实际业务超时量级匹配统计滞后是设计前提平均延迟统计与实时延迟存在时间差因此必须保留timeout_cl_max_concurrency这个宽泛的兜底并发防止突发慢请求瞬间堆积极端情况下平均延迟会超过超时时间此时限流器依靠并发为 1 的请求始终放行来维持统计自愈拒绝语义达到限制后请求会被直接拒绝框架返回ELIMIT客户端应将其视为可重试到其他实例的信号同时根据 src/brpc/server.h 的说明与固定max_concurrency一致访问内置服务builtin services不受此限流限制。综上brpc 的 Timeout Concurrency Limiter 以统计平均延迟 vs 请求超时时间为决策核心配合采样窗口、失败惩罚与兜底并发提供了一种贴合真实业务延迟波动、无需人工频繁调参的 method 级自适应限流方案。在开启前建议结合brpc::TimeoutConcurrencyConf为关键 method 单独标定超时与兜底并发并通过timeout_cl_enable_error_punish、timeout_cl_fail_punish_ratio调节对失败请求的敏感度从而在及时处理更多请求与避免过载雪崩之间取得平衡。【免费下载链接】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),仅供参考
返回列表