go限流首选rate.limiter,需复用实例并按key分组;熔断推荐gobreaker,避免手写缺陷;http中限流返回429,熔断返回503;grpc限流须到method级,阈值需压测调优。

限流用 golang.org/x/time/rate 最直接
Go 标准库生态里,rate.Limiter 是最轻量、最可控的限流方案,不需要引入第三方依赖,适合 HTTP 中间件或 RPC 方法级限流。
常见错误是直接在 handler 里 new 一个 rate.Limiter,导致每个请求都新建 limiter,完全失效。正确做法是复用单个实例,比如按用户 ID 或服务端点做 key 分组管理:
-
limiter := rate.NewLimiter(rate.Every(time.Second/10), 10)表示 1 秒最多 10 次(允许突发 10 次) - 若需区分调用方,用
sync.Map缓存 per-key limiter,注意控制 map size 防止内存泄漏 - 调用
limiter.Allow()判断是否放行;更推荐limiter.ReserveN(time.Now(), n)+.OK(),能精确控制多 token 消费(如上传文件按大小计数)
熔断用 sony/gobreaker 而非自己手写状态机
自己实现熔断器容易忽略超时恢复、半开状态探测失败回退、统计窗口滑动等细节,gobreaker.CircuitBreaker 经过生产验证,配置清晰。
关键配置项影响大:
立即学习“go语言免费学习笔记(深入)”;
-
MaxRequests设为 0 表示半开状态只允许 1 个试探请求;设为正数(如 3)则允许多个并发试探,但失败任一即退回熔断 -
Timeout不是请求超时,而是“熔断持续时间”,到期自动进半开;太短会导致频繁抖动,太长影响服务恢复速度 -
ReadyToTrip函数决定何时熔断,默认是连续失败 5 次,建议改用滑动窗口失败率(如 10 秒内失败率 > 60%),需自行包装go-breaker的CountError和CountSuccess
HTTP 中间件里组合限流 + 熔断要分清责任边界
限流拦的是“太多请求”,熔断拦的是“下游已不可用”,两者逻辑不能混用。典型错误是把熔断失败当成限流拒绝返回 429 Too Many Requests,这会误导上游重试策略。
- 限流中间件应放在路由匹配后、业务处理前,用
http.Handler包裹,拒绝时返回429并带Retry-After - 熔断应包裹实际的下游调用(如
http.Do或 gRPC client 方法),失败时返回503 Service Unavailable,并记录gobreaker.State变化日志 - 不要在同一个中间件里既调
limiter.Allow()又调cb.Execute()—— 限流失败不该触发熔断统计,否则低频故障也会抬高失败率
gRPC 场景下限流粒度必须到 method 级
gRPC 的 UnaryServerInterceptor 可以拿到 info.FullMethod,这是唯一可靠的限流标识。按 service 或整个 server 限流在微服务中基本没意义。
实操要点:
- 用
strings.Split(info.FullMethod, "/")提取方法名,例如/user.UserService/GetProfile→GetProfile,再查对应 limiter - 避免用
context.WithValue传 limiter 实例,容易泄漏;改用 interceptor 外部初始化好的map[string]*rate.Limiter+sync.RWMutex - 如果使用
grpc-gateway,HTTP 层限流和 gRPC 层限流要独立配置 —— 同一业务在两种协议下 QPS 基线可能差 3–5 倍










