后端可靠性设计:熔断器、幂等性与中间件编排

工作期间除了写业务代码,也接触了几个微服务可靠性相关的设计模式。这部分是"代码能跑"和"生产环境能抗"之间的差距。

一、熔断器

问题

微服务链路上,下游挂了一个环节就逐级拖垮上游:

调用

调用

超时堆积

线程/连接耗尽

整条链路崩溃

服务 A

服务 B

服务 C
❌ 响应变慢

雪崩

熔断器就是让调用方在发现下游异常时主动拒绝、快速失败,保护自己不被拖垮。熔断器是客户端中间件,保护的是调用方,不是下游。

Google SRE 自适应算法

Kratos 默认用的是 Google SRE 的自适应算法,公式很简单:

requests < K × successes
  • requests:窗口内总请求数
  • successes:窗口内成功请求数
  • K:容忍系数,默认 2

当 requests ≥ K × successes(失败率太高),开始按概率拒绝请求。

比传统的"连续失败 N 次就熔断"好用的地方在于自适应:不用设固定阈值,拒绝概率随成功率平滑变化,流量高时自动更敏感,流量低时更宽容。

三态模型

SRE 条件触发
requests ≥ K × successes

冷却时间到
默认 10s

探测请求成功

探测请求失败

CLOSED

OPEN

HALF_OPEN

HALF_OPEN 是自动恢复的关键——冷却期后放少量请求试探,下游恢复了就切回 CLOSED,没恢复就回到 OPEN。整个过程不需要人工介入。

Kratos 集成

http.NewClient(
    http.WithMiddleware(circuitbreaker.Client()),
)

一行代码启用。和重试中间件配合时,熔断器放重试前面——先判断熔没熔断,再决定要不要重试。

参数调优上,支付类的高敏感业务把 Success 调高(0.95),日志类的低敏感业务调低(0.7),流量突增时降低 Request 阈值让熔断更敏感。


二、幂等性设计

问题

网络不可靠,客户端超时重试可能导致同一个请求被服务端执行多次。发奖、扣款这类写操作,重复执行就是事故。

幂等性:同一个操作执行一次或多次,结果相同。

实现方案

存在

不存在

请求携带幂等键

查 Redis
key 存在?

直接返回缓存结果

获取分布式锁

执行业务逻辑

结果写入 Redis
设 TTL 30min

释放锁

返回结果

几个要点:

  • 幂等键用 SHA256 对关键参数(用户 ID + 操作类型 + 唯一标识)做哈希
  • Redis 缓存执行结果,TTL 覆盖重试窗口
  • 分布式锁防止并发下同一幂等键被同时执行

适用场景不限于发奖——订单创建、支付回调、积分变更,所有不允许重复的写操作都适用。


三、中间件编排顺序

Kratos 中间件链按声明顺序执行,编排会直接影响行为:

grpc.WithMiddleware(
    recovery.Recovery(),        // 1. 最内层:panic 恢复
    logging.Client(),           // 2. 日志
    circuitbreaker.Client(),    // 3. 熔断器
    retry.Client(),             // 4. 重试
)

先熔断再重试,避免熔断期间还在做无效重试。recovery 在最内层保证任何 panic 都能兜住。

四、总结

这几个模式用同一个思路串起来:假设一切都可能出问题。下游会挂就加熔断,请求会重试就做幂等,代码会 panic 就加 recovery,网络会超时就设 deadline。

学校里写的项目一般不关心这些,也没有此类场景,但生产环境里这些都是大概率事件。从写好一个功能到写出一个可靠的功能,是最近最大的认知升级。

更多推荐