微服务化听起来很美——每个服务专心做自己的事,对外暴露接口,各司其职。但真到了生产环境,事情就没那么简单了。服务的自动扩缩容、熔断、限流,这些都不是锦上添花,而是生存刚需。随着业务膨胀,服务数量猛增,依赖关系像蜘蛛网一样复杂。一个服务要完成某个逻辑,往往得调用好几个其他服务。这时候,但凡其中一个依赖掉了链子,就可能引发连锁反应——雪崩效应,最终整个系统全挂。
微服务之间用 RPC 或者 HTTP 通信时,大家通常都会设置调用超时和失败重试,觉得这样就能确保成功。初衷没问题,可如果不配合熔断和限流,这些机制恰恰成了雪崩的导火索。
举个典型的场景。假设两个高并发服务 A 和 B,它们分别依赖 C 和 D,而 C 和 D 又都依赖 E 服务。

正常情况下,A 和 B 不断调用 C、D 处理请求、返回数据。现在 E 服务突然挂了,C 和 D 的超时和重试机制就会疯狂触发。

新的调用源源不断进来,C 和 D 对 E 的请求大量积压,每个都在等待、重试。很快,内存和 CPU 被耗尽,C 和 D 也跟着 down 掉。

接下来轮到 A 和 B 重复同样的剧本——资源耗尽,纷纷倒下。最终整个服务集群全面瘫痪。

常见的雪崩诱因无非这几类:
- 程序 Bug 导致服务不可用或响应缓慢;
- 缓存击穿,所有流量直接打到后端数据库或某个关键服务;
- 访问量突然激增,超出系统容量;
- 硬件故障,这只能归结为运气问题了。
虽然雪崩的触发方式五花八门,但保证服务不宕机、流畅运行,是必须扛起来的责任。应对雪崩,业界已经积累了不少成熟的保护方案。
服务的横向扩充
现在有很多工具能帮我们兜底。流量大了,横向扩充服务是常见做法。比如 k8s,不仅能维持服务运行状态,还能让服务自动扩缩容。对于突发流量,这套打法确实管用。但横向扩充也有天花板——如果 E 服务响应时间始终过长,即便你加再多副本,雪崩的风险依然存在,只是时间问题。

限流
限制客户端调用频率是最直接的限流手段。比如规定每秒最多处理 200 个请求,超过的直接拒绝。常见的实现之一是令牌桶算法:以固定速度往桶里放令牌,客户端请求时必须先拿到令牌才能被处理;桶里的令牌用完了,就直接拒绝访问。

熔断
熔断的思路是在客户端控制对依赖的访问。如果发现某个依赖不可用了,就不再调用它,直接返回错误或者走降级逻辑。开源的库比如 hystrix-go(这也是接下来准备深入源码分析的一个库),就很好地实现了熔断和降级。它的核心是一组阈值:最大并发数、错误率百分比、熔断尝试恢复时间等。通过这些阈值,熔断器在三个状态间切换:
- 关闭状态:允许调用依赖;
- 打开状态:不允许调用依赖,直接返回错误或执行 fallback;
- 半开状态:根据
熔断尝试恢复时间自动进入,允许尝试调用一次,成功则回到关闭状态,失败则继续打开。

具体的实现细节,以及对 hystrix-go 的源码分析,会在后续的文章中展开。
