beego中限流应通过自定义middleware在controller.run前实现,按ip用rate.limiter配合sync.map存储实例,避免全局共享;qps与burst需合理设置(如api接口50~200 qps、burst=2~3倍),敏感接口需更严并加验证码;集群环境用redis原子操作(incr+expire或cl.throttle),失败时fail-open;务必返回429响应头、记录详细日志并测试验证。

Beego 中用 middleware 做限流最直接
Beego 本身不内置限流中间件,但它的 Controller.Run 前可插自定义 middleware,这是加限流逻辑的唯一合理入口。别试图在 Prepare() 里硬塞计数器——它不拦截重复请求,也不保证并发安全。
推荐用内存型限流器(如 golang.org/x/time/rate 的 Limiter)配合 sync.Map 或 map + sync.RWMutex 存储每个 IP 的限流器实例。按 IP 限流是防刷最基础也最有效的维度。
- 每个请求进来先提取
c.Ctx.Input.IP(),注意 Nginx 转发时要读X-Real-IP或X-Forwarded-For - 用 IP 作 key 查找对应的
rate.Limiter实例;不存在则新建(比如 100 次/秒,burst=200) - 调
limiter.AllowN(time.Now(), 1)判断是否放行;拒绝时直接c.Abort("429")并返回Too Many Requests - 避免用全局单个
rate.Limiter——它会把所有 IP 当成一个桶,起不到区分限流作用
rate.Limiter 的参数怎么设才不误杀正常用户
限流不是越严越好。rate.NewLimiter(100, 200) 表示每秒最多 100 次,但允许突发 200 次(burst)。如果 burst 设太小(比如 10),前端重试、浏览器预加载、HTTP/2 多路复用都可能触发误限流。
- 普通 API 接口建议:QPS=50~200,burst=QPS×2~3(留出客户端抖动余量)
- 登录、注册等敏感接口必须更严:QPS=5,burst=10,并额外增加设备指纹或验证码兜底
- 不要对
/static/、/healthz等路径限流,应在 middleware 中用c.Ctx.Input.URL()提前跳过 - 注意
rate.Limiter不是线程安全的,每个 IP 必须独占一个实例,不能多个 goroutine 共享同一个
Redis 方案适合分布式部署但引入新故障点
单机 Beego 应用用内存限流足够;一旦上集群,就必须用 Redis 做统一计数。这时不能再依赖 rate.Limiter,得换原子操作:INCR + EXPIRE 组合,或直接用 Redis 的 CL.THROTTLE(Redis 6.2+)。
- 用
INCR key返回当前值,再EXPIRE key 1设置 1 秒过期(注意EXPIRE要在INCR后立即执行,否则有竞态) - 若返回值 > 100,则
c.Abort("429");否则放行 - Beego 里推荐封装成独立函数,例如
checkRateLimit(c *context.Context, key string, max int, windowSec int) bool - Redis 连接失败时必须降级为放行(fail-open),否则整个服务不可用;别设成 fail-closed
别漏掉关键细节:Header、日志和测试验证
限流生效后,用户需要知道被限了,运维需要知道谁被限了。这两件事不做,等于白加。
- 响应头里加
c.Ctx.Output.Header("X-RateLimit-Limit", "100")、X-RateLimit-Remaining和X-RateLimit-Reset,前端可据此做友好提示 - 每次触发限流,用
beego.Info记日志,至少包含 IP、URL、User-Agent;别只打 “rate limit hit” 这种无用信息 - 写个简单脚本用
ab -n 200 -c 50 http://localhost:8080/api/test验证是否真生效;观察返回码分布和响应头变化 - 特别注意:Beego 的
Recovery中间件默认捕获 panic,但它在限流中间件之后执行——如果限流逻辑自己 panic 了,用户看到的是 500 而非 429
限流逻辑本身容易写,难的是边界处理:IP 伪造、代理链、burst 与 QPS 的平衡、降级策略、可观测性。这些不提前想清楚,上线后第一波爬虫来就暴露问题。











