用Go和Echo实现限流网关

举报
静水流深-云海 发表于 2026/09/10 14:34:31 2026/09/10
【摘要】 用Go的Echo 框架写一个按ip限流的中间件,借助x/time/rate令牌桶保护后端,超过配额直接回429。

后端服务最怕的不是流量大,而是被人用脚本刷。一个没做限制的接口,分分钟被爬到宕机。我给自己的小服务前面加了一层用 Echo 写的限流网关,按 IP 限速,思路很简单但很管用。顺手记一下实现。

image.png

一、为什么不直接上网关

像 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)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。