Gin框架防刷限流中间件基于令牌桶算法的实现

星婷同学_7504

星婷同学_7504

2026-09-02

867人浏览

原创

因为单机内存令牌桶无法共享状态,多实例部署下各节点独立计数导致限流失效;redis限流必须用lua脚本原子执行读、判、扣、设过期全流程,避免incr+expire竞态引发超发或key永久残留。

gin框架防刷限流中间件基于令牌桶算法的实现

为什么不用内存版令牌桶做生产限流

因为单机内存计数器在多实例部署下完全失效。你本地测试 TokenBucketLimiter 能跑通,上线后流量打到不同机器,每个实例各自维护一套桶,等于没限。真实场景中,哪怕只是两台负载均衡后的 Gin 服务,限流阈值就会翻倍——用户刷个接口,轻松绕过限制。

常见错误现象:429 Too Many Requests 偶发不触发、压测时限流形同虚设、日志里看到同一 IP 在不同节点反复通过。

  • 只适合开发联调或单实例离线工具类服务
  • 无法共享状态,不满足分布式一致性要求
  • 重启即清空,不具备持久化兜底能力

Redis 实现令牌桶必须用 EVAL 原子脚本

直接用 INCR + EXPIRE 组合有竞态:两次 Redis 请求之间可能被其他客户端插入,导致超发令牌。gin-vue-admin 的 SetLimitWithTime 就踩了这个坑——它用 TxPipeline 包装,但 pipeline 不保证原子性,只是批量发送。

正确做法是把“读当前令牌数、判断是否够用、扣减/填充、设置过期”全部塞进 Lua 脚本,由 Redis 单线程执行:

local key = KEYS[1]
local limit = tonumber(ARGV[1])
local capacity = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local rate = tonumber(ARGV[4])

local tokens = redis.call("GET", key)
if not tokens then
  redis.call("SET", key, capacity, "EX", 60)
  return 1
end

tokens = tonumber(tokens)
if tokens > 0 then
  redis.call("DECR", key)
  return 1
else
  return 0
end
  • 别信“pipeline 很快所以差不多”的说法——高并发下毫秒级窗口就足够出问题
  • EVAL 脚本里不能依赖 Redis 服务端时间(如 TIME),必须由客户端传入 now 时间戳校准
  • 桶容量和填充速率要按业务峰值反推,比如每秒 50 次请求,建议桶容量设为 100,避免突发打满

IP 粒度限流不够用,得支持多维 key 构造

纯靠 c.ClientIP() 做 key,在 NAT 环境或 CDN 后面会误伤大量用户。更危险的是,攻击者只要换一个出口 IP 就能绕过——实际攻防中,这几乎是默认操作。

Gin框架 1.9.0
Gin框架 1.9.0

Gin框架 1.9.0版本源码包下载,版本号 1.9.0,适合需要 sonic JSON 支持、路由修复和内容协商改进的 Go Web 开发场景。

下载

你应该组合多个维度生成限流 key:

  • 优先取 X-Real-IP 或 X-Forwarded-For 头(需 Nginx 透传并校验可信代理)
  • 对登录用户追加 user_id(从 JWT 或 session 解析),实现“每人每小时 100 次”
  • 对敏感接口加入 path 或 method:PATH,避免 /login 和 /pay 共用同一桶
  • 必要时加设备指纹(如 UA + IP 哈希),但注意 GDPR 合规风险

示例 key:rate:uid_12345:POST:/api/v1/withdraw,比 rate:192.168.1.1 精准且可控得多。

漏桶 vs 令牌桶:别被原理图带偏,看实际响应曲线

文档里画的“漏桶恒速流出”是理想模型。真实 Redis 实现中,漏桶用 go.uber.org/ratelimit 是基于 client-side sleep 模拟的,服务端根本没做任何控制——它只是告诉客户端“你该等多久”,而客户端完全可以忽略这个提示继续发包。

真正起作用的只有服务端拦截型方案,而令牌桶天然适配这种模式:每次请求来都尝试 DECR,成功才放行,失败直接 429。漏桶若想真落地,得配合 Redis List + BRPOP 定时消费,复杂度陡增,还引入延迟。

  • 选令牌桶不是因为它“高级”,而是它在 Redis 场景下更容易写出正确、低延迟的原子逻辑
  • 所谓“容忍突发”,本质是桶容量给了缓冲空间;这个空间大小比算法名字重要得多
  • 如果你发现限流后仍有请求穿透,先检查 key 是否重复、脚本是否真被执行、Redis 连接是否复用错实例

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

2024.05.11

609

5

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

2025.12.18

604

18

ThinkPHP中间件机制与请求拦截处理实践
ThinkPHP中间件机制与请求拦截处理实践

本专题围绕 ThinkPHP 中间件体系展开,深入讲解中间件的定义、注册与执行流程。内容包括全局中间件与路由中间件的区别、请求前后处理逻辑、自定义中间件开发以及权限验证与日志处理应用。通过实际案例,帮助开发者掌握中间件在项目中的核心作用与最佳实践。

2026.03.31

418

17

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

2023.08.14

5376

4

Go Web框架Gin接口开发与中间件设计实践
Go Web框架Gin接口开发与中间件设计实践

本专题围绕 Go 在 Web 后端开发中的主流框架 Gin 展开,系统讲解高性能接口开发与中间件机制设计。内容涵盖路由分组、请求绑定、参数校验、统一响应封装、日志与鉴权中间件实现,以及接口限流与异常处理策略。通过实战项目案例,帮助开发者构建结构清晰、性能优良的 Go Web 服务体系,提升接口开发效率与系统可维护性。

2026.03.19

297

21

FrankenPHP集成Laravel详细教程
FrankenPHP集成Laravel详细教程

本专题提供FrankenPHP集成Laravel的详细配置指南,全面解析运行原理、开发环境搭建、Caddyfile配置、Octane工作模式、数据库连接、队列任务、定时任务和生产环境优化,解决部署过程中常见的报错与兼容性问题。

2026.10.08

0

20

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

2026.09.30

120

10

LLVM RISC-V参数配置教程
LLVM RISC-V参数配置教程

本专题介绍LLVM对RISC-V基础ISA和扩展的支持方式,涵盖RV32、RV64、标准扩展、实验性扩展、厂商扩展、-menable-experimental-extensions和版本差异。

2026.09.30

100

14

LLVM IR中间表示入门指南
LLVM IR中间表示入门指南

本专题整理LLVM IR的核心概念,包括中间表示作用、模块结构、函数、基本块、SSA形式、类型系统和常见语法,帮助新手理解LLVM编译流程中的关键层。

2026.09.30

80

12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Gin框架快速入门指南
Gin框架快速入门指南

共0课时 | 0人学习

Gin框架官方中文文档
Gin框架官方中文文档

共0课时 | 0人学习

Go语言Gin框架全套实战教程
Go语言Gin框架全套实战教程

共0课时 | 0人学习