几十个服务互相调用,怎么不互相拖垮?—— 治理与稳定性
服务拆多了,治理问题接踵而至:服务怎么找到彼此?出了问题怎么发现?一个服务挂了怎么不拖垮别人?这一篇讲四件事:注册发现、可观测性、优雅上下线、熔断限流降级。
注册发现:服务怎么找到彼此
服务实例是动态变化的(扩缩容、重启),调用方不能写死 IP。所以需要一个注册中心维护"服务名 → 实例列表"。流程是:实例启动时注册 + 心跳续期,调用方从注册中心拉列表,再负载均衡地选一个实例调用。
健康检查有两种:主动上报(定时心跳,超时剔除)和被动检查(调用失败上报)。实例下线要"先注销再停服务"。选型上,etcd 强一致(Raft)带租约,consul 带健康检查,ZooKeeper 老牌 CP 强一致——掌握原理和选型对比即可。
可观测性:出了问题怎么发现
几十个服务,出问题靠"人肉看日志"是不现实的。可观测性有三大支柱:
- 日志(Logging):离散事件,看"发生了什么"。
- 指标(Metrics):聚合数值(QPS/延迟/错误率),看"整体趋势"。
- 链路(Tracing):一次请求跨服务的路径和耗时,看"卡在哪一环"。
一句话:日志看单点,指标看全局,链路看路径。
优雅上下线:服务起停不能"硬来"
服务上线如果一启动就接流量,可能被瞬间打满(还没预热完);下线如果直接杀进程,正在处理的请求就丢了。
优雅下线的流程是:收到停止信号 → 停止接新请求 → 处理完在途请求 → 注销服务 → 退出。Go 里用 signal.NotifyContext + http.Server.Shutdown(ctx) 实现。优雅上线则是先完成依赖初始化、缓存预热,再注册接流量。
熔断、限流、降级:不拖垮系统的三件套
下游服务挂了,如果调用方还一直傻等,就会级联雪崩——一个服务拖垮整个系统。三件套各自防一件事:
- 限流:控制进入的请求量,超阈值拒绝(令牌桶/漏桶/滑动窗口)。
- 熔断:下游连续失败超阈值,直接快速失败,不再调用。状态机是
Closed(正常)→ Open(熔断)→ Half-Open(探测)→ 恢复或重新熔断。 - 降级:压力大或依赖故障时,关闭非核心功能,保核心。
还有一条铁律:所有 RPC 必须设超时(否则一个慢服务拖死所有调用方),而重试必须配合幂等(否则重复执行副作用)。
串起来
微服务的治理是一套组合拳:注册发现解决"找到彼此",可观测性解决"发现问题",优雅上下线解决"起停不丢请求",熔断限流降级解决"不互相拖垮"。这些稳定性的基本功,比业务代码更能体现一个后端工程师的成熟度。
下一篇是 S4 的面试题集。