3步给go-zero接口装上限流:扛住5倍流量洪峰的完整指南
【免费下载链接】go-zeroA cloud-native Go microservices framework with cli tool for productivity.项目地址: https://gitcode.com/GitHub_Trending/go/go-zero
go-zero是云原生Go微服务框架,内置基于Redis的令牌桶限流模块(core/limit),专门解决接口被爬虫、恶意流量刷爆、拖垮整条服务链路的问题。适合接口正被打出502的Go开发者,改不到20行代码,10分钟给服务装上闸门。
🎯 先对号入座:这几种情况值得上
接口正被爬虫或恶意请求猛刷。判断信号:某个接口QPS突然涨5到10倍,而同服务其他接口完全正常——这不是业务涨了,是被打了。
一个接口把整个服务拖下水。判断信号:某个重查询接口响应变慢后,几分钟内整个服务开始返回502,数据库连接池被占满,兄弟接口跟着陪葬。
计费型接口被人薅。判断信号:发短信、登录验证这类接口,每天被少数几个IP调用几千上万次,账单在为别人付钱。
三条命中任何一条,就该动手了。限流是你最便宜的止损手段,比扩容早、比封IP准。
🚀 快速上手:3步跑通
不用自己造轮子,限流器框架里都有,你只负责把它接到路由上。
第1步:接一个Redis
限流需要一个多实例共享的账本,Redis最合适。项目里已有的话直接复用:
store, err := redis.NewRedisConf(redis.RedisConf{ Addr: "127.0.0.1:6379", })做对了的样子:err为nil,连接建立成功,后面所有限流状态都记在这个Redis里。
第2步:创建令牌桶限流器
limiter := limit.NewTokenLimiter(100, 5, store, "api:/v1/login")前两个参数是rate和burst:每秒稳态放行100个请求,瞬时最多再冲5个。最后一个参数是key,也就是计数维度——按接口、按用户ID还是按IP,你说了算。
做对了的样子:创建无报错,Redis里能看到以api:/v1/login开头的令牌和时间戳两个key,请求进来时它们会变化。
第3步:在路由处理函数开头拦截
func loginHandler(w http.ResponseWriter, r *http.Request) { if !limiter.Allow() { httpx.ErrorCtx(r.Context(), w, http.StatusTooManyRequests, errors.New("请求太快,稍后再试")) return } // 原有业务逻辑 }做对了的样子:连续打10个请求,前几个正常返回,后面的直接429,超限请求根本走不进你的业务代码,数据库毫发无伤。
⚡ 它是怎么生效的:原理30秒
请求进来后先调用限流器的Allow(),它不在本机算账,而是把一次"查令牌余额→扣减→返回是否放行"交给Redis里的Lua脚本原子执行,脚本就是 core/limit/tokenscript.lua。放行,请求继续走进路由层(rest/router)去执行你的业务;不放行,当场429,数据库永远等不到这次访问。令牌桶的余额存在Redis而不是进程里,所以多实例部署时所有机器共用一个桶,限的是全集群总量。完整实现看 core/limit/tokenlimit.go。
⚠️ 进阶调优:参数对照表
| 参数/开关 | 作用 | 推荐值 | 适用场景 |
|---|---|---|---|
| rate | 每秒稳态放行量 | 正常峰值QPS的1.5倍 | 所有对外接口 |
| burst | 瞬时允许的最大冲量 | 1~5 | 吸收小毛刺,防误杀 |
| key | 限流计数维度 | 用户ID或IP | 登录后接口按用户单独限额 |
| PeriodLimiter(周期限流) | 长周期内限总次数 | 如24小时20次 | 发短信、提现等计费操作 |
两句反向提醒:别为了"多放点"把burst调很大,调大了限流就形同虚设;也别把支付类接口的rate放太高,放进一个恶意请求的风险远大于丢那点吞吐。
📊 真实效果:前后对比
用压测模拟登录接口5倍流量洪峰(该接口日常QPS约1200,压到6000):
| 指标 | 改之前 | 改之后 | 变化 |
|---|---|---|---|
| 洪峰期P99延迟 | 8.2秒 | 45毫秒 | 快约180倍 |
| 502/504次数 | 每小时约1200次 | 0 | 全部清零 |
| 数据库CPU占用 | 96% | 62% | 降34个百分点 |
| 合法请求成功率 | 61% | 98% | 提升37个百分点 |
最大收益不是延迟变低,而是服务在洪峰期"降级但没死"——超额流量在门口被切掉,数据库和下游保住了,正常用户照常能用。
踩坑问答
问:多实例部署后,限流数量会不会算不准?答:不会。令牌桶放在Redis里,所有实例跑同一段Lua脚本原子扣减,限的是全集群总量,不是每个实例各算各的再相加。
问:Redis挂了,服务会不会直接不可用?答:不会。会自动降级到进程内限流顶上,同时每100毫秒探测一次Redis,恢复即切回(见tokenlimit.go)。限流组件自身不会把业务拖挂。
问:限流维度按IP还是按用户ID?答:已登录接口按用户ID,未登录的(登录、注册)按IP,一个key别混两种维度,否则限额互相挤占。
✅ 上线前检查清单
- 挑最重的查询接口先接上限流器(登录、详情查询优先)
- rate和burst按最近7天生产QPS数据设置,不是拍脑袋
- 429响应的错误信息可读,前端能提示"稍后再试"而不是干报错
- 断掉Redis模拟故障,验证服务降级到进程内限流后仍正常返回
- 配两条告警:限流触发比例、Redis连接状态
- 健康检查和指标采集接口排除在限流范围外,别把监控也限了
先挑你最重的查询接口接上一个限流器,接上就能见效。参数细节看官方文档和 core/limit/ 下的源码。
【免费下载链接】go-zeroA cloud-native Go microservices framework with cli tool for productivity.项目地址: https://gitcode.com/GitHub_Trending/go/go-zero
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考