Skip to content
Go
限流、熔断与过载保护

限流、熔断与过载保护

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   # 毫秒,默认 3000

gRPC 客户端

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 等是否端口冲突需要检查