我们的重试循环加剧了服务中断。熔断器阻止了级联故障。

发布日期:2026-05-21 10:01:17   浏览量 :4
发布日期:2026-05-21 10:01:17  
4

2026西湖龙井茶官网DTC发售:茶农直供,政府溯源防伪到农户家 

几周前,Anthropic 曾出现一个持续 22 分钟的时间窗口,期间返回了大量 5xx 错误响应。这并非完全的服务中断,而是服务降级。

我们的代理服务配置了一种重试策略,在遇到 5xx 错误时会进行退避并重新尝试。由于只有六个工作线程,且我设置的重试预算上限过高,导致我们重新发起失败调用的速度几乎与应用程序接口(API)返回错误的速度一样快。当应用程序接口(API)恢复时,我们积压了大量正在进行中的重试请求,这直接导致我们再次触发了速率限制。

这一错误决策的总代价是:大约 18,000 次被浪费的 Anthropic 调用,以及在对方服务恢复后额外增加的 9 分钟恢复时间。虽然没有任何用户可见的系统崩溃,但我对此感到懊恼。

第二天,我编写了 llm-circuit-breaker。它非常小巧,整个 Rust 代码包不超过 400 行。它与 llm-retry 配合使用。

状态机

              失败次数 >= 阈值
   +-------+ ----------------------> +------+
   | 关闭  |                          | 开启 |
   +-------+ <---------------------- +------+
       ^      半开状态成功             |
       |                               |
       |     半开状态失败              v
       |   <----------------------- +-----------+
       +---------------------------- | 半开状态  |
            冷却时间结束              +-----------+
  • 关闭:调用正常通过。失败次数会被累计。
  • 开启:调用立即返回 BreakerError::Open,而不实际请求应用程序接口(API)。经过一段冷却时间后,断路器将转换到半开状态。
  • 半开状态:仅允许一次试探性调用通过。如果成功,则回到关闭状态。如果失败,则回到开启状态,并重置冷却时间。

这就是整个状态机。没有漏桶算法,也没有复杂的滑动窗口。其功能足以防止失控的重试将部分服务中断演变为完全服务中断。

代码示例

use llm_circuit_breaker::{Breaker, BreakerConfig};
use std::time::Duration;

let breaker = Breaker::new(BreakerConfig {
    failure_threshold: 5,
    success_threshold: 1,
    cooldown: Duration::from_secs(30),
});

let result = breaker.call(|| async {
    client.messages().create(payload).await
}).await;

免责声明:本文内容来自互联网,该文观点不代表本站观点。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请到页面底部单击反馈,一经查实,本站将立刻删除。

关于我们
热门推荐
合作伙伴
免责声明:本站部分资讯来源于网络,如有侵权请及时联系客服,我们将尽快处理
支持 反馈 订阅 数据
回到顶部