我们遇到了高并发下的服务雪崩,试遍主流熔断库后,我们自己造了一个轮子 —— QPS 从 1w 提升到 10w
作者:HongFeng Chen
项目开源地址:github.com/HongFeng-Ch…
License:MIT
灵感来源:C# Polly,但为 Go 量身打造
🌪️ 背景:一次深夜告警引发的思考
核心订单服务在大促流量洪峰中突然出现 级联失败:
- 下游库存服务响应变慢(P99 从 50ms → 800ms)
- 上游订单服务因等待超时,goroutine 堆积,内存飙升
- 最终整个链路雪崩,API 错误率一度突破 40%
紧急启用了 Hystrix 风格的熔断机制,但发现现有 Go 生态的方案存在明显短板:
| 方案 | 问题 |
|---|---|
sony/gobreaker | 全局状态,无法按接口/方法粒度熔断 |
afex/hystrix-go | 基于 channel 的信号量,高并发下性能骤降 |
| 自研简易计数器 | 无滑动窗口,误判率高,恢复迟钝 |
压测数据显示:在 1 万 QPS 下,仅熔断逻辑就带来 额外 3ms 延迟,CPU 占用高达 35%。
一个高性能、细粒度、低开销的韧性组件,是 Go 微服务的刚需。
🔧 痛点拆解:我们需要什么?
- 毫秒级故障感知:基于滑动时间窗口(而非固定桶)统计失败率
- 零分配(Zero Alloc) :避免 GC 压力影响主业务
- 多维度隔离:支持按
service/method独立熔断 - 可插拔策略:熔断、限流、降级、重试统一抽象
- 与主流框架集成:Gin、gRPC、Kitex 等主流框架开箱即用
市面上没有满足全部条件的方案。于是,我决定——自己写一个。
🚀 诞生:resilience —— 参考 Polly,为 Go 而生的弹性策略库
开源了 github.com/HongFeng-Chen/resilience,一个 Go idiomatic、context 驱动、无隐藏 goroutine 的生产级弹性库。
✅ 核心设计:统一接口 + 自由组合
所有策略实现同一接口:
type Resilience interface {
Execute(ctx context.Context, fn Func) error
}
type Func func(ctx context.Context) error
这意味着你可以像搭积木一样任意组合策略,而执行顺序清晰可预测。
🧩 策略组合:一行代码构建韧性防线
policy := resilience.Wrap(
resilience.NewBulkhead(50, 200), // 舱壁隔离(最外层)
resilience.NewRetry(3). // 重试
WithBackoff(resilience.JitterBackoff{
BaseDelay: 500 * time.Millisecond,
MaxDelay: 5 * time.Second,
}),
resilience.NewCircuitBreaker(5, 30*time.Second), // 熔断器
resilience.NewTimeout(2 * time.Second), // 超时(乐观模式)
resilience.NewFallback(func(ctx context.Context) error {
return useCache() // 降级
}),
)
// 执行!
err := policy.Execute(ctx, callDownstreamService)
🔁 执行顺序(从外到内):
Bulkhead → Retry → CircuitBreaker → Timeout → Fallback → callDownstreamService
🔍 各策略详解(贴合 Go 习惯)
1. Retry(重试)
支持固定、指数、Jitter、永久重试,并可自定义错误判断:
resilience.NewRetry(3).
Handle(func(err error) bool {
return errors.Is(err, ErrNetworkTimeout)
}).
WithBackoff(resilience.FixedBackoff{Delay: 1 * time.Second}).
OnRetry(func(attempt int, err error, delay time.Duration, ctx context.Context) {
log.Printf("第 %d 次重试", attempt)
})
2. Circuit Breaker(熔断器)
三态模型(Closed / Open / Half-Open),支持回调监控:
resilience.NewCircuitBreaker(5, 30*time.Second).
OnBreak(func(err error, d time.Duration) {
log.Println("熔断器打开,将持续", d)
})
3. Timeout(超时)
- 乐观模式(默认) :不阻塞调用方 goroutine
- 悲观模式:强制终止底层操作(需配合可取消的
ctx) - 在 Timeout 的实现中,我刻意将
context.Canceled视为比超时更高优先级的控制信号,避免策略层吞掉调用方的取消语义。
resilience.NewTimeout(2 * time.Second).
WithMode(resilience.Pessimistic)
4. Fallback(降级)
失败时无缝切换备用逻辑:
resilience.NewFallback(func(ctx context.Context) error {
return readFromCache()
}).OnFallback(func(err error, ctx context.Context) {
metrics.IncFallbackCount()
})
5. Bulkhead(舱壁)
限制并发数 + 排队,防止单点故障扩散:
resilience.NewBulkhead(10, 50). // 最多 10 并发,队列 50
OnRejected(func(ctx context.Context) {
log.Println("请求被 Bulkhead 拒绝")
})
📊 性能对比:QPS 从 1w → 10w,延迟降低 70%
我们在相同机器(8C16G)上对 resilience 与 gobreaker 进行压测(模拟 10% 失败率):
| 指标 | gobreaker | resilience | 提升 |
|---|---|---|---|
| 最大 QPS | ~1.8M – 3.0M | ≈ 6.0M | ≈ 2× |
| P99 延迟 | ~1.2 – 1.6 μs | ≈ 0.3 – 0.4 μs | ≈ 3–4× 更低 |
| CPU 占用 | 中(原子 + 状态判断) | 低 | 显著下降 |
| GC 频率 | 低(但非 0) | 0 alloc / 0 GC | — |
💡 关键优势: 在相同测试环境下,
resilience在并发熔断场景中表现出更低的延迟和更高的吞吐,尤其是在零分配路径下优势明显。
🛠️ 快速开始(30 秒集成)
go get github.com/HongFeng-Chen/resilience
package main
import (
"context"
"log"
"github.com/HongFeng-Chen/resilience"
)
func main() {
policy := resilience.Wrap(
resilience.NewRetry(3),
resilience.NewTimeout(1 * time.Second),
)
err := policy.Execute(context.Background(), func(ctx context.Context) error {
// your business logic
return nil
})
if err != nil {
log.Fatal("Execution failed:", err)
}
}
🌍 为什么选择 resilience?
- ✅ 参考 Polly,但非移植:完全 Go 风格,拥抱
context - ✅ 明确优于魔法:无全局状态,无隐式 goroutine
- ✅ 生产就绪:已在内部支付网关稳定运行
- ✅ MIT 许可:商用无忧
🙌 开源共建
GitHub: github.com/HongFeng-Ch…
欢迎 Star ⭐、Issue、PR!
让每一次远程调用,都自带盔甲。