brpc/docs/cn/avalanche.md
2022-12-14 20:13:26 +08:00

22 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

“雪崩”指的是访问服务集群时绝大部分请求都超时,且在流量减少时仍无法恢复的现象。下面解释这个现象的来源。
当流量超出服务的最大qps时服务将无法正常服务当流量恢复正常时小于服务的处理能力积压的请求会被处理虽然其中很大一部分可能会因为处理的不及时而超时但服务本身一般还是会恢复正常的。这就相当于一个水池有一个入水口和一个出水口如果入水量大于出水量水池子终将盛满多出的水会溢出来。但如果入水量降到出水量之下一段时间后水池总会排空。雪崩并不是单一服务能产生的。
如果一个请求经过两个服务情况就有所不同了。比如请求访问A服务A服务又访问了B服务。当B被打满时A处的client会大量超时如果A处的client在等待B返回时也阻塞了A的服务线程常见且使用了固定个数的线程池常见那么A处的最大qps就从**线程数 / 平均延时**,降到了**线程数 / 超时**。由于超时往往是平均延时的3至4倍A处的最大qps会相应地下降3至4倍从而产生比B处更激烈的拥塞。如果A还有类似的上游拥塞会继续传递上去。但这个过程还是可恢复的。B处的流量终究由最前端的流量触发只要最前端的流量回归正常B处的流量总会慢慢降下来直到能正常回复大多数请求从而让A恢复正常。
但有两个例外:
1. A可能对B发起了过于频繁的基于超时的重试。这不仅会让A的最大qps降到**线程数 / 超时**还会让B处的qps翻**重试次数**倍。这就可能陷入恶性循环了:只要**线程数 / 超时 \* 重试次数**大于B的最大qpsB就无法恢复 -> A处的client会继续超时 -> A继续重试 -> B继续无法恢复。
2. A或B没有限制某个缓冲或队列的长度或限制过于宽松。拥塞请求会大量地积压在那里要恢复就得全部处理完时间可能长得无法接受。由于有限长的缓冲或队列需要在填满时解决等待、唤醒等问题有时为了简单代码可能会假定缓冲或队列不会满这就埋下了种子。即使队列是有限长的恢复时间也可能很长因为清空队列的过程是个追赶问题排空的时间取决于**积压的请求数 / (最大qps - 当前qps)**如果当前qps和最大qps差的不多积压的请求又比较多那排空时间就遥遥无期了。
了解这些因素后可以更好的理解brpc中相关的设计。
1. 拥塞时A服务最大qps的跳变是因为线程个数是**硬限**单个请求的处理时间很大程度上决定了最大qps。而brpc server端默认在bthread中处理请求个数是软限单个请求超时只是阻塞所在的bthread并不会影响为新请求建立新的bthread。brpc也提供了完整的异步接口让用户可以进一步提高io-bound服务的并发度降低服务被打满的可能性。
2. brpc中[重试](client.md#重试)默认只在连接出错时发起,避免了流量放大,这是比较有效率的重试方式。如果需要基于超时重试,可以设置[backup request](client.md#重试)这类重试最多只有一次放大程度降到了最低。brpc中的RPC超时是deadline超过后RPC一定会结束这让用户对服务的行为有更好的预判。在之前的一些实现中RPC超时是单次超时*重试次数,在实践中容易误判。
3. brpc server端的[max_concurrency选项](server.md#限制最大并发)控制了server的最大并发当同时处理的请求数超过max_concurrency时server会回复client错误而不是继续积压。这一方面在服务开始的源头控制住了积压的请求数尽量避免延生到用户缓冲或队列中另一方面也让client尽快地去重试其他server对集群来说是个更好的策略。
对于brpc的用户来说要防止雪崩主要注意两点
1. 评估server的最大并发设置合理的max_concurrency值。这个默认是不设的也就是不限制。无论程序是同步还是异步用户都可以通过 **最大qps \* 非拥塞时的延时**(秒)来评估最大并发,原理见[little's law](https://en.wikipedia.org/wiki/Little%27s_law)这两个量都可以在brpc中的内置服务中看到。max_concurrency与最大并发相等或大一些就行了。
2. 注意考察重试发生时的行为特别是在定制RetryPolicy时。如果你只是用默认的brpc重试一般是安全的。但用户程序也常会自己做重试比如通过一个Channel访问失败后去访问另外一个Channel这种情况下要想清楚重试发生时最差情况下请求量会放大几倍服务是否可承受。