用Go和Echo实现限流网关
【摘要】 用Go的Echo 框架写一个按ip限流的中间件,借助x/time/rate令牌桶保护后端,超过配额直接回429。
后端服务最怕的不是流量大,而是被人用脚本刷。一个没做限制的接口,分分钟被爬到宕机。我给自己的小服务前面加了一层用 Echo 写的限流网关,按 IP 限速,思路很简单但很管用。顺手记一下实现。

一、为什么不直接上网关
像 APISIX、Kong 这种现成网关当然能限流,但那是要单独部署维护一套组件的。我的场景只是单服务、单节点,引入一整套网关纯属杀鸡用牛刀。用 Echo 中间件在应用层先拦一道,零额外依赖,部署也简单。等真到了多服务规模,再换专业网关不迟。
二、Echo 中间件怎么拦
核心是用 golang.org/x/time/rate 给每个 IP 一个令牌桶,请求来了先 Allow(),没令牌就直接 429:
package main
import (
"sync"
"time"
"github.com/labstack/echo/v4"
"golang.org/x/time/rate"
)
var limiters sync.Map
func getLimiter(ip string) *rate.Limiter {
l, _ := limiters.LoadOrStore(ip, rate.NewLimiter(rate.Limit(5), 10))
return l.(*rate.Limiter)
}
func RateLimit(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ip := c.RealIP()
if !getLimiter(ip).Allow() {
return c.JSON(429, map[string]string{"msg": "请求太频繁,稍后再试"})
}
return next(c)
}
}
func main() {
e := echo.New()
e.Use(RateLimit)
e.GET("/api", func(c echo.Context) error {
return c.JSON(200, map[string]string{"ok": "1"})
})
e.Start(":8080")
}
rate.NewLimiter(5, 10) 表示每秒放 5 个令牌、桶容量 10,突发能扛一下。Allow() 是非阻塞的,正好适合 HTTP 中间件。
三、令牌桶还是计数器
| 方案 | 平滑度 | 实现难度 | 备注 |
|---|---|---|---|
| 固定窗口计数器 | 低(有临界突刺) | 低 | 用 Redis incr 即可 |
| 滑动窗口 | 中 | 中 | 精度高但占内存 |
| 令牌桶(x/time/rate) | 高 | 低 | 本例采用,自带平滑 |
中小服务用令牌桶性价比最高,库都给你写好了。
四、两个注意点
RealIP()拿到的是内网 IP:服务前面套了 Nginx 时,得配TrustProxy并取X-Forwarded-For,否则所有请求都算同一个 IP,限流反而把正常用户全挡了。- sync.Map 不会清理:长时间运行,限流器会越积越多。生产环境可以加个定时清理,或者干脆用 Redis 存计数做全局限流。
小结
限流的本质是"宁可错杀少量正常请求,也别让恶意流量打垮服务"。Echo 中间件 + 令牌桶这个组合,几十行代码就能给接口加一道闸门,比上整套网关轻多了。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)