限流、熔断与过载保护
go-zero 内置了完整的弹性治理体系,所有机制自动生效、零配置可用,构成了"面向故障编程"的核心竞争力。
多层防御体系
请求 → 限流(MaxConns) → 认证(Authorize) → 熔断(Breaker) → 降载(Shedding) → 超时(Timeout) → 业务逻辑限流(Rate Limiting)
go-zero 提供两种限流算法和一个并发限制器。
Period Limit — 滑动窗口限流(分布式)
基于 Redis + Lua 脚本 实现,保证多实例下的原子操作。
import "github.com/zeromicro/go-zero/core/limit"
l := limit.NewPeriodLimit(
60, // 窗口大小(秒)
100, // 配额(每个窗口允许的请求数)
redis.NewRedis("127.0.0.1:6379", ""),
"periodlimit:resource",
)
// 获取令牌
code, err := l.Take("resourceKey")
switch code {
case limit.Allowed: // 0 — 未超配额,放行
case limit.HitQuota: // 2 — 刚好触及阈值
case limit.OverQuota: // 1 — 已超配额,应拒绝
}Token Limit — 令牌桶限流(本地)
允许瞬时突发流量,适合有一定弹性需求的服务。
import "github.com/zeromicro/go-zero/core/limit"
limiter := limit.NewTokenLimiter(
100, // 每秒生成 token 速率
200, // 桶容量(允许的最大突发量)
)
if limiter.Allow() {
// 放行
} else {
// 拒绝
}MaxConnsHandler — 并发连接限制
HTTP 中间件级别,限制最大并发连接数:
MaxConns: 10000 # 默认 10000超过限制返回 HTTP 503 Service Unavailable。
限流算法对比
| 算法 | 适用场景 | 分布式 | 突发流量 |
|---|---|---|---|
| Period Limit | API 限流、用户级别限流 | ✅(依赖 Redis) | ❌ 不支持 |
| Token Limit | 本地限流、瞬时突发 | ❌ 单机 | ✅ 支持 |
| MaxConns | 并发连接控制 | ❌ 单机 | N/A |
熔断(Circuit Breaker)
go-zero 的熔断器基于 Google SRE 自适应限流算法,在 RPC 客户端自动为每个下游服务创建独立熔断器。
三态模型
┌───── 错误率高于阈值 ─────┐
│ │
┌──────▼──────┐ ┌───────┴──────┐
│ Closed │ │ Open │
│ 关闭(正常) │ │ 打开(拒绝) │
└──────┬──────┘ └───────┬──────┘
│ │
│ 冷却时间过后进入
│ │
│ ┌────────▼────────┐
└─────────────────┤ Half-Open │
成功则恢复 │ 半开(探测) │
└─────────────────┘| 状态 | 含义 | 行为 |
|---|---|---|
| Closed | 正常 | 正常处理请求,持续统计错误 |
| Open | 故障 | 所有请求立即拒绝(快速失败) |
| Half-Open | 冷却后 | 放行一个探测请求;成功→Closed,失败→Open |
核心算法:Google SRE 自适应限流
被拒绝请求数 = max(0, (请求总数 − K × 成功数) / (请求总数 + 1))默认 K = 1.5。K 值越大,熔断器越不敏感。
自动集成
- zrpc 客户端:
BreakerInterceptor自动为每个下游 endpoint 创建独立熔断器 - HTTP 服务:
BreakerHandler默认启用
自定义熔断
import "github.com/zeromicro/go-zero/core/breaker"
// 对非标准组件使用熔断(DB、外部 API 等)
err := breaker.DoWithAcceptable("redis-fallback", func() error {
return db.Query(ctx, ...)
}, func(err error) bool {
// 自定义:哪些错误计入失败计数
// 排除业务错误如 ErrNotFound
return !errors.Is(err, ErrNotFound)
})禁用熔断
Breaker: false过载降载(Load Shedding)
go-zero 基于 CPU 使用率 + 在途请求数 实现自适应降载。
监测机制
| 指标 | 监测方式 | 默认阈值 |
|---|---|---|
| CPU 使用率 | 每 250ms 采样一次 | 90%(Shedding.CpuThreshold: 900) |
| 请求通过率 | 滑动窗口统计 | 动态计算 |
配置
Shedding:
CpuThreshold: 900 # 0-1000,默认 900(即 90% CPU)当降载激活时
| 服务 | 行为 |
|---|---|
| HTTP | 返回 503 Service Unavailable |
| gRPC | 返回 codes.ResourceExhausted |
调优建议
| 服务类型 | 推荐 CPU 阈值 | 原因 |
|---|---|---|
| 无状态服务 | 900-950 | CPU 上升通常表示负载过高 |
| CPU 密集型 | 700-800 | 需要更多剩余算力 |
| IO 密集型 | 900-950 | CPU 不是瓶颈 |
自定义降载处理
// 通过中间件自定义降载时的响应
server.Use(rest.SheddingHandler(func(w http.ResponseWriter, r *http.Request) {
httpx.WriteJson(w, http.StatusServiceUnavailable, map[string]string{
"code": "503",
"msg": "service is overloaded, please retry later",
})
}))超时控制
HTTP 服务
Timeout: 3000 # 毫秒,默认 3000gRPC 客户端
ctx, cancel := context.WithTimeout(l.ctx, 2*time.Second)
defer cancel()
resp, err := l.svcCtx.UserRpc.GetUser(ctx, &user.GetUserReq{Id: userId})🔬 实际超时取客户端 context 超时和服务端配置超时的最小值。如果客户端先超时,服务端也会收到 cancel 通知。
Panic 恢复
go-zero 自动捕获所有 panic,返回 500 Internal Server Error,不会导致整个进程崩溃。
弹性机制综合使用
典型场景:高并发下的下单接口
func (l *OrderLogic) Create(req *types.CreateOrderReq) (*types.CreateOrderResp, error) {
// 1. 本地限流
if !l.limiter.Allow() {
return nil, errs.ErrRateLimit
}
// 2. 分布式限流(按用户级别)
code, _ := l.periodLimit.Take(fmt.Sprintf("order:user:%d", req.UserId))
if code != limit.Allowed {
return nil, errs.ErrRateLimit
}
// 3. 调用 RPC(自动熔断 + 超时)
ctx, cancel := context.WithTimeout(l.ctx, 2*time.Second)
defer cancel()
stock, err := l.svcCtx.StockRpc.CheckStock(ctx, ...)
if err != nil {
return nil, err
}
// 4. 核心业务逻辑
return l.createOrder(ctx, req.UserId, stock)
}保护外部依赖
// 发送邮件(非核心,用熔断保护)
breaker.DoWithAcceptable("send-email", func() error {
return email.Send(to, subject, body)
}, func(err error) bool {
// 超时不触发熔断,网络错误才触发
return !errors.Is(err, context.DeadlineExceeded)
})默认启用的中间件一览
| 中间件 | 功能 | 控制方式 |
|---|---|---|
RecoverHandler |
Panic 恢复 | 始终启用 |
TimeoutHandler |
请求超时 | Timeout 配置 |
MaxConnsHandler |
并发连接限制 | MaxConns 配置 |
BreakerHandler |
熔断 | Breaker: false 禁用 |
SheddingHandler |
过载降载 | Shedding 配置 |
StatHandler |
请求统计 | 始终启用 |
💡 这些中间件按顺序执行,越靠前的中间件先被执行。
常见陷阱
| 陷阱 | 说明 |
|---|---|
| 🚨 Period Limit 依赖 Redis | Redis 不可用时限流失效,不会自动拒绝 |
| 🚨 熔断 K 值设置过小 | K 太小导致熔断器过于敏感,正常波动触发熔断 |
| 🚨 熔断器排除业务错误 | ErrNotFound 等业务错误不应触发熔断,用 acceptable 回调过滤 |
| 🚨 CPU 阈值在容器环境不准 | 容器 CPU 限制可能导致降载逻辑不准确,需要压测验证 |
| 🚨 忘记设置 RPC 超时 | gRPC 默认无限等待,不设超时导致调用方被挂住 |
| 🚨 panic 恢复后令牌桶未归还 | 极端情况下 panic 恢复路径跳过限流 DeferFunc → 令牌泄漏 |
| 🚨 同机多服务端口冲突 | MaxConns、Prometheus 等是否端口冲突需要检查 |