字
字节笔记本
2026年2月22日
gin-redis-ip-limiter:基于 Redis 的 Gin 框架 IP 限流中间件
本文介绍 gin-redis-ip-limiter,一个基于 Redis 的 Gin 框架 IP 限流中间件。该项目使用滑动窗口算法实现高效的请求限流,帮助开发者保护 API 接口免受恶意请求和流量冲击。
项目简介
gin-redis-ip-limiter 是一个开源的 Go 语言中间件项目,由 Salvatore Giordano 开发维护。该项目基于 Gin Web 框架,利用 Redis 的有序集合(Sorted Set)数据结构实现滑动窗口限流算法。截至目前,该项目在 GitHub 上已获得 24 stars,12 个 fork,主要使用 Go 语言编写。
该项目的核心设计灵感来自 Engagor 博客的滑动窗口限流文章,使用 go-redis 作为 Redis 客户端库。
核心特性
- 滑动窗口限流算法:基于 Redis 有序集合实现精确的滑动窗口计数,避免固定窗口的临界突刺问题
- IP 级别的限流控制:根据客户端 IP 地址进行请求限制,有效防止单 IP 的恶意请求
- 可插拔设计:只需传入 Redis 客户端即可使用,支持自定义限流键、请求上限和时间窗口
- Gin 框架原生支持:作为 Gin 中间件使用,集成简单,符合 Gin 的 HandlerFunc 规范
- 轻量级实现:核心代码仅约 40 行,无冗余依赖,性能高效
技术栈
- Go - 主要编程语言
- Gin - Web 框架
- Redis - 分布式缓存和限流数据存储
- go-redis - Redis 客户端库
安装指南
前置要求
- Go >= 1.11(支持 Go Modules)
- Redis 服务器
安装步骤
bash
go get -u github.com/Salvatore-Giordano/gin-redis-ip-limiter/快速开始
go
package main
import (
"time"
"github.com/Salvatore-Giordano/gin-redis-ip-limiter"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis"
)
func main() {
r := gin.Default()
// 配置限流中间件
r.Use(iplimiter.NewRateLimiterMiddleware(
redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 1,
}),
"general", // Redis 键前缀
200, // 请求上限
60*time.Second, // 滑动窗口时长
))
// ... 你的路由定义
r.Run(":8080")
}使用示例
场景 1:API 接口限流
go
package main
import (
"net/http"
"time"
"github.com/Salvatore-Giordano/gin-redis-ip-limiter"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis"
)
func main() {
r := gin.Default()
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// 为 API 路由组添加限流
api := r.Group("/api")
api.Use(iplimiter.NewRateLimiterMiddleware(
redisClient,
"api",
100, // 每分钟最多 100 次请求
60*time.Second,
))
{
api.GET("/users", getUsers)
api.POST("/orders", createOrder)
}
r.Run(":8080")
}
func getUsers(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"users": []string{"user1", "user2"}})
}
func createOrder(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"order_id": "12345"})
}场景 2:不同接口差异化限流
go
func main() {
r := gin.Default()
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// 登录接口:每分钟 5 次(防止暴力破解)
loginLimiter := iplimiter.NewRateLimiterMiddleware(
redisClient,
"login",
5,
60*time.Second,
)
// 普通接口:每分钟 200 次
generalLimiter := iplimiter.NewRateLimiterMiddleware(
redisClient,
"general",
200,
60*time.Second,
)
r.POST("/login", loginLimiter, handleLogin)
r.GET("/data", generalLimiter, getData)
r.Run(":8080")
}API 参考
NewRateLimiterMiddleware
创建一个新的限流中间件。
go
func NewRateLimiterMiddleware(
redisClient *redis.Client,
key string,
limit int,
slidingWindow time.Duration,
) gin.HandlerFunc| 参数 | 类型 | 说明 |
|---|---|---|
redisClient | *redis.Client | Redis 客户端实例 |
key | string | Redis 键前缀,数据存储格式为 ip:key |
limit | int | 在滑动窗口内允许的最大请求数 |
slidingWindow | time.Duration | 滑动窗口的时间长度 |
限流响应
当请求超过限制时,中间件会返回 HTTP 429 (Too Many Requests) 状态码:
json
{
"status": 429,
"message": "too many request"
}实现原理
该中间件使用 Redis 有序集合(Sorted Set)实现滑动窗口限流:
- 清理过期数据:每次请求时,删除窗口时间外的旧记录
- 统计当前请求数:查询当前 IP 在窗口期内的请求数量
- 判断是否限流:如果请求数达到上限,返回 429 状态码
- 记录新请求:将当前请求时间戳添加到有序集合
- 设置过期时间:为 Redis 键设置与窗口时长相同的过期时间
这种实现方式相比固定窗口更加平滑,能够有效处理临界时刻的请求突刺问题。
注意事项
- Redis 连接:确保 Redis 服务器可用,中间件初始化时会检查连接
- 时间同步:分布式部署时,确保各服务器时间同步,避免限流不准确
- 内存占用:Redis 会为每个 IP 存储时间戳数据,注意监控内存使用
- 错误处理:Redis 连接失败会导致中间件 panic,建议在初始化时做好检查
项目链接
分享: