text: ---
  title: "微服务的战争：级联故障和雪崩"
  date: 2020-08-25T21:08:39+08:00
  toc: true
  images:
  tags: 
    - 微服务
  ---
  
  > “微服务的战争” 是一个关于微服务设计思考的系列题材，主要是针对在微服务化后所出现的一些矛盾/冲突点，不涉及具体某一个知识点深入。如果你有任何问题或建议，欢迎随时交流。
  
  在 [微服务的战争：统一且标准化](https://eddycjy.com/posts/microservice/standardization/) 中，经过好几周与不同业务组不同事业部的跨部门讨论后，终于把初始的标准化方案给定下来了，大家欢快的使用起了内部的统一框架，疯狂的创建起了新服务，没隔多久服务调用链就变成了下图：
  
  ![image](https://image.eddycjy.com/97128d96a1a4b8ba1f765bf30134f529.jpg)
  
  服务间存在多次内部调用，服务 A =》服务 B =》服务 C =》服务D，而 服务 E =》 服务 B，服务 F =》服务 E，也就是存在着多个流量入口，且依赖相同的服务。
  
  ## 背景
  
  服务与服务中，总存在业务服务，公共服务，基础服务等类型。但在某一个夜晚，突然发现 BFF 调用后端服务开始逐渐不正常，客户给你截图反馈问题，你发现有点问题：
  
  ![image](https://image.eddycjy.com/844d0d5a730a4c692d7d96912e1d710c.jpg)
  
  单从表现来看，你发现是 BFF 调用服务 A 极度缓慢，也不知道怎么了...正当以为是服务 A 出问题，想着万能重启一下时。你在日志平台和链路追踪系统一看，发现了大量的错误日志和缓慢，让你略微震惊，一时间不知道从何下手。
  
  这可怎么办？
  
  ## 级联故障和雪崩
  
  实际上这是一次很经典的级联故障，最终导致系统雪崩的情景再现。单从上述拓扑来看，问题点之一在于服务 B：
  
  ![image](https://image.eddycjy.com/51b18e7c80833fbe1dc13b3a3290940b.jpg)
  
  服务 B 本身作为服务 A 和服务 F 的两个流量入口必经之处，想必至少是一个公共服务，但他也依赖了其他多个服务。因此若服务 C 和服务 D 其中一个有问题，在没有熔断措施的情况下，就出现级联故障，系统逐渐崩盘，最后雪崩：
  
  ![image](https://image.eddycjy.com/2e7b0d94c32f3bd8f3f7168046671d2b.jpg)
  
  服务 D 所依赖的外部接口出现了故障，而他并没有做任何的控制，因此扩散到了所有调用到他的服务，自然也就包含服务 B，因此最终出现系统雪崩。
  
  这种最经典的是出现在默认 Go http client 调用没有设置 Timeout，从而只要出现一次故障，就足矣让记住这类 “坑”，毕竟崩的 ”慢“，错误日志还多。
  
  ## 解决方法
  
  常见的方式是**根据特定的规则/规律进行熔断和降级**，避免请求发生堆积：
  
  - 超时时间控制。
  
  - 慢调用比例。
  
  - 错误比例。
  
  - 自适应（例如：负载情况等）。
  
  当然，这也只是壮士断腕，后续措施还包含监控告警，通知对应的开发人员来处理。且需提前对被降级的模块进行业务逻辑进行处理等等，这样才能够比较柔和且快速地度过这一次危机。
  
  ## 总结
  
  在分布式应用中，级联故障和雪崩是非常常见的，一些开发同学在模块设计时可能并没有意识到这块的问题，在微服务化后会一个不留神就碰到，因为其调用链变得特别的长且多。因此建议配套设施和限流熔断措施都应该及时跟上，否则面对一大堆的错误日志还是很无奈的。
  
  同时，监控告警的建设也要做，因为在危机出现时，有一个 HTTP 调用的 P95/P99 告警出现，那就比较舒心了，直接 root cause。