TP8.0 基于 Token Bucket 算法的漏桶限流实现【自研】

落瑶君_8338

落瑶君_8338

2026-07-14

1017人浏览

原创

在thinkphp 8.0中需基于redis+lua自研token bucket限流:桶容量100、速率20/s,键名含路由与ip;懒加载补令牌、lua原子扣减、支持拒绝/排队/动态降级三策略,并通过ab压测与时间回退验证正确性。

tp8.0 基于 token bucket 算法的漏桶限流实现【自研】

在 ThinkPHP 8.0 项目中实现高并发场景下的平滑限流,需避免简单 sleep 或计数器式硬拦截导致的响应延迟与用户体验断层。Token Bucket 算法能兼顾突发流量容忍度与长期速率可控性,但 TP8.0 官方未内置该算法实现,必须基于 Redis + 时间差懒加载机制自研落地。

初始化令牌桶配置与 Redis 连接

在 app/common/behavior/RateLimitBehavior.php 中新建行为类,构造函数内完成基础参数注入与 Redis 实例获取:

使用 【TP8.0 的 Container::getInstance()->get('cache')->handler()】 获取原生 Redis 实例,不可用 Cache::store('redis') —— 后者封装层会干扰原子操作和 pipeline 执行。

设置桶容量(capacity)为 100,令牌生成速率为每秒 20 个,即 refillIntervalMs = 50ms;桶键名格式为 rate:token:{route}:{ip},确保路由+客户端 IP 双维度隔离。

懒加载式令牌补给逻辑

每次请求到达时才计算应补充的令牌数,不依赖定时任务:

读取桶当前状态:$data = $this->redis->hGetAll($key),其中包含 tokens、last_refill 两个字段;

若 last_refill 为空,说明桶首次使用,直接设 tokens = $this->capacity 并写入 last_refill = time();

否则计算时间差:$deltaMs = (microtime(true) * 1000) - $data['last_refill'],再按速率折算应补令牌:$newTokens = floor($deltaMs / $this->refillIntervalMs);

【注意:必须用 floor 而非 round,避免因浮点误差多补 1 个令牌导致超发】

原子化消费与状态更新

使用 Redis pipeline 批量执行三步操作,防止并发竞争:

第一步:检查当前 tokens 是否 ≥ 1;

第二步:若满足,则执行 HINCRBY $key tokens -1 消耗令牌;

ThinkPHP5.1企业站点快速开发-源码课件
ThinkPHP5.1企业站点快速开发-源码课件

ThinkPHP5.1企业站点快速开发

下载

第三步:更新 HSET $key last_refill (microtime(true)*1000);

整个 pipeline 必须包裹在 eval 脚本或 WATCH-MULTI-EXEC 中——但 TP8.0 默认 Redis 驱动不支持 MULTI 嵌套,因此改用 Lua 脚本一次性完成判断+扣减+更新,脚本哈希值缓存复用。

限流响应与降级策略

方法一:直接拒绝

当 Lua 返回 false 时,调用 response()->fail('Too many requests', 429) 并设置 Header:X-RateLimit-Remaining: 0、X-RateLimit-Reset: time()+1;

方法二:排队等待

对非敏感接口(如列表页),启用阻塞模式:循环执行 Lua 脚本,每次失败后 usleep(10000)(10ms),最多重试 3 次,超时则降级为方法一;

方法三:动态降级

当连续 5 次限流触发且 Redis RTT > 20ms,自动切换至本地内存计数器(基于 Swoole Table 或 APCu),避免 Redis 故障引发全站雪崩。

测试验证流程

第一步:启动 ab 压测工具,执行 ab -n 200 -c 50 http://test.local/api/v1/list;

第二步:观察 Redis 中对应 key 的 tokens 字段变化趋势,应呈锯齿状缓慢下降→快速回升→趋近于 capacity;

第三步:检查日志中 429 响应占比,理想值为 max(0, (总请求数 - capacity) / 总请求数),误差超过 ±3% 需校准 refillIntervalMs;

第四步:手动修改系统时间向前拨动 10 秒,验证时间回退时 last_refill 不被覆盖,tokens 不负增长。

相关文章

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

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

下载

相关标签:

redis lua thinkphp

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

相关专题

更多
登录token无效
登录token无效

登录token无效解决方法:1、检查token的有效期限,如果token已经过期,需要重新获取一个新的token;2、检查token的签名,如果签名不正确,需要重新获取一个新的token;3、检查密钥的正确性,如果密钥不正确,需要重新获取一个新的token;4、使用HTTPS协议传输token,建议使用HTTPS协议进行传输 ;5、使用双因素认证,双因素认证可以提高账户的安全性。

2023.09.14

13566

6

登录token无效怎么办
登录token无效怎么办

登录token无效的解决办法有检查Token是否过期、检查Token是否正确、检查Token是否被篡改、检查Token是否与用户匹配、清除缓存或Cookie、检查网络连接和服务器状态、重新登录或请求新的Token、联系技术支持或开发人员等。本专题为大家提供token相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.14

1625

5

token怎么获取
token怎么获取

获取token值的方法:1、小程序调用“wx.login()”获取 临时登录凭证code,并回传到开发者服务器;2、开发者服务器以code换取,用户唯一标识openid和会话密钥“session_key”。想了解更详细的内容,可以阅读本专题下面的文章。

2023.12.21

3697

3

token什么意思
token什么意思

token是一种用于表示用户权限、记录交易信息、支付虚拟货币的数字货币。可以用来在特定的网络上进行交易,用来购买或出售特定的虚拟货币,也可以用来支付特定的服务费用。想了解更多token什么意思的相关内容可以访问本专题下面的文章。

2024.03.01

5861

5

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

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

2023.08.14

5796

4

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

2026.10.16

0

10

OpenJDK怎么配置Java开发环境
OpenJDK怎么配置Java开发环境

怎样用OpenJDK搭建开发环境?本专题介绍版本选择、下载安装、环境变量配置和编译运行验证,帮助你理清JDK的作用及基本配置流程,并通过简单程序检查环境是否可用,为后续项目开发做好准备。

2026.10.16

0

10

OpenJDK怎么部署Java应用
OpenJDK怎么部署Java应用

Java项目开发完成后,怎样部署到目标环境并正常运行?本专题介绍OpenJDK应用部署流程,涵盖运行环境准备、版本兼容检查、应用打包、启动参数配置和日志查看,帮助你理清部署前后的操作顺序,减少环境差异造成的运行问题。

2026.10.16

0

10

Revel框架模板渲染与表单验证
Revel框架模板渲染与表单验证

本专题围绕Revel框架视图层开发,介绍模板语法、变量输出、布局复用、表单提交、参数绑定与验证规则,帮助开发者完成安全规范的页面交互。

2026.10.16

0

15

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程