
翻译:https://www.javacodegeeks.com/2025/10/microservices-madness-practical-patterns-that-keep-your-services-resilient.html
微服务就像那种在聚会上玩得很嗨,但第二天却让人头疼的朋友。它们承诺灵活性、可扩展性,以及让团队快速行动的自由。但任何与它们打过交道的人都知道故事的另一面:网络故障、不可预测的负载,以及那些行为像小孩一样的服务——不可靠且容易崩溃。
那么,如何在这种疯狂中生存下来?答案是弹性。不是你在励志名言中看到的那种,而是实用的模式,确保当你的系统某一部分跌倒时,其他部分不会跟着一起崩溃。
在单体应用中,如果出了问题,至少它只在一个地方出问题。而在微服务中,故障会传播。一个缓慢的支付服务可能会拖垮结账流程,进而拖垮订单系统,最终让用户只能盯着转圈的加载图标。
你会遇到如下问题:
这就是我们正在面对的混乱——也是为什么弹性模式不是可选项,而是必需品。
让我们抛开理论,聊聊在实践中真正有效的东西。
想象一下,你给一个关机的朋友打电话。试了五次后,你会停下来,对吧?这就是熔断器的作用。它发现一个服务失败,就停止发送请求,保护你的系统不再浪费资源。相反,它可以返回一个快速的“降级”响应,或者直接快速失败。
为什么它有用:防止级联故障,保持系统响应能力。
怎么做: 使用像 Resilience4j 或 Spring Cloud Circuit Breaker 这样的库,轻松实现。
在船上,舱壁将船体分隔成多个部分,这样即使一个舱进水,也不会沉船。在微服务中,你也可以应用相同的理念,通过隔离资源来实现。例如,为不同的任务分配不同的线程池。如果一个池子被堵住了,其他的仍能正常运行。
为什么它有用: 系统某一部分的请求洪流不会拖垮其他部分。
失败后重试是有道理的——有时候只是网络抽风了一下。但无脑重试可能会压垮你正试图访问的服务。这就是为什么我们要加入指数退避(每次等待时间更长)和抖动(加入随机性,避免重试风暴)。
为什么它有用: 给服务恢复的空间,而不会让情况更糟。
永远不要相信一个没有设置超时的服务调用。如果你不设置,就可能让用户永远等下去。配合降级使用:如果推荐服务挂了,就显示“热销商品”作为替代。
为什么它有用: 即使系统状态不佳,用户仍然能得到有用的结果。
紧密耦合的 REST 调用意味着一个服务必须依赖另一个服务“此刻可用”。事件驱动的消息传递可以 loosen 这种耦合。使用像 Kafka 或 RabbitMQ 这样的工具,服务可以发布事件,其他服务在有能力时做出响应。
为什么它有用: 服务之间不会互相阻塞,你可以更优雅地缓冲和重试。
下面是一个快速判断何时使用哪种模式的表格:
模式 | 何时使用 | 常用工具 |
|---|---|---|
熔断器 | 服务持续失败或响应太慢 | Resilience4j、Spring CB |
舱壁隔离 | 某个客户端/服务可能耗尽所有资源 | 线程池、K8s 配额 |
带退避的重试 | 故障看起来是暂时的(网络、限流) | Resilience4j Retry |
超时 + 降级 | 你宁愿给用户“点什么”,而不是什么都没有 | HTTP 客户端、Spring |
事件消息传递 | 紧耦合让你的服务脆弱不堪 | Kafka、RabbitMQ |
事实是:模式只能带你走这么远。如果你看不到系统中发生了什么,你就是在盲开。这就是为什么可观测性是必需品。
弹性不仅仅是处理故障——更是知道何时、为何以及如何发生故障。
最终,弹性不仅仅是让服务“活着”。它是关于优雅地失败。也许这意味着返回缓存数据,也许意味着限制某个用户的流量,以便其他用户不受影响。
微服务并不会让系统变得更简单。它们将复杂性从代码转移到了通信、网络和数据中。诀窍不是与疯狂对抗——而是接受它,并在设计时就考虑失败。
微服务本质上就是混乱的。 混乱与控制之间的区别,在于你为失败做了多好的准备。 熔断器、重试、超时和消息传递并不能消除疯狂——但它们让你能够幸存下来。