接口防刷限流,这个Java滑动窗口工具类11个方法不怕校时

简介: 这是一个基于滑动窗口与`nanoTime`单调时钟的轻量级Java限流工具类(JDK17+),支持单/批量配额、剩余查询、等待时长计算、手动重置及空闲清理,适用于接口防刷、短信频控等场景。

📋 目录

一、工具类功能

按「资源 key + 时间窗口内最大请求数」放行请求,接口防刷、验证码频率、第三方配额保护都能用,时间基准取 nanoTime 单调时钟,系统校时与时间回拨都不会让限流突然失效。

二、所需环境

JDK 17或以上

三、方法与调用示例(按需调用)

🔢 方法序号:1

🎯 方法用途:

  • 默认单例上按「资源 key + 窗口内最大请求数」放行,一次消耗 1 个配额
  • 验证码发送频率、接口防刷这类不想自己管实例的场景直接用它

🧩 方法定义:public static boolean tryAcquire(String key, int maxRequests, Duration window)

🗣️ 入参解释:public static boolean tryAcquire(资源key, 阈值, 窗口)

📤 返回值解释:boolean,拿到配额返回 true,超限返回 false

💡 边界口径:

  • 先剔除窗口外的旧记录再判额度,是滑动窗口而不是固定窗口
  • 判定用 System.nanoTime() 单调时钟,系统时间回拨、NTP 校时都不影响窗口
  • 额度为 0、key 为 null 或空白、窗口为 0 或负数都抛 IllegalArgumentException
  • 这是单机限流,多实例部署时全局放行量是阈值乘实例数

🧪 方法示例:

  • 60 秒窗口额度 2,tryAcquire(phone, 2, SMS_WINDOW) 第 1 次拿到 true
  • 同一 key 连打到第 3 次拿到 false,窗口里的记录还在就不放行
  • 额度传 0 抛 maxRequests 必须大于 0,当前为0
  • key 传空白串抛 key 不能为null或空白

🔢 方法序号:2

🎯 方法用途:

  • 默认单例上一次批量消耗多个配额,按笔计额度时用
  • 一次调用要发 5 条短信、扣 5 个配额这类场景用它

🧩 方法定义:public static boolean tryAcquire(String key, int permits, int maxRequests, Duration window)

🗣️ 入参解释:public static boolean tryAcquire(资源key, 本次配额数, 阈值, 窗口)

📤 返回值解释:boolean,额度够就整笔扣掉并返回 true

💡 边界口径:

  • 额度不足时整笔不扣,不会先扣掉一部分再说
  • permits 大于 maxRequests 属于参数非法,直接抛异常,这种请求永远过不去
  • permits 小于 1 同样抛 IllegalArgumentException

🧪 方法示例:

  • tryAcquire("batch-api", 5, 8, IP_WINDOW) 额度 8 一次扣 5,拿到 true
  • 同一 key 再来一笔要 4 个,只剩 3 个,拿到 false
  • acquire("y", 0, 3, window) 抛 permits 必须大于 0,当前为0
  • permits 比总额度还大时抛 permits 不能大于 maxRequests,当前为5 > 3,这样的请求永远无法通过

🔢 方法序号:3

🎯 方法用途:

  • 取默认单例,静态入口操作的就是它
  • 想用 remaining、retryAfter、清理与观测这些实例方法时从它拿

🧩 方法定义:public static RateLimiterUtils defaultLimiter()

🗣️ 入参解释:public static RateLimiterUtils defaultLimiter()(无入参)

📤 返回值解释:RateLimiterUtils,进程内唯一的默认实例

💡 边界口径:

  • 返回的是同一个对象,多次取回的结果可直接比引用
  • 静态 tryAcquire 写的桶在这里都能看见
  • 需要按业务隔离限流桶时另用 new RateLimiterUtils(),别共用这个单例

🧪 方法示例:

  • defaultLimiter().size() 在静态入口刚打过一次之后拿到 1
  • defaultLimiter().retryAfter(phone, 2, SMS_WINDOW) 拿到 PT59.9946621S
  • 连续两次 defaultLimiter() 比引用,拿到 true

🔢 方法序号:4

🎯 方法用途:

  • 在自己的实例上消耗 1 个配额,桶与其他实例互不影响
  • 按业务模块隔离限流范围时用,比如接口限流和短信限流各一套

🧩 方法定义:public boolean acquire(String key, int maxRequests, Duration window)

🗣️ 入参解释:public boolean acquire(资源key, 阈值, 窗口)

📤 返回值解释:boolean,放行返回 true

💡 边界口径:

  • 同一 key 走 ConcurrentHashMap.compute 串行化,不同 key 互不阻塞
  • 多线程并发不会各拿一条队列,也不会超放
  • 静态入口与新建实例是两套桶,同一个 key 各限各的

🧪 方法示例:

  • 独立实例上同一 IP 连打 5 次,额度 8,acquire(ip, 8, IP_WINDOW) 拿到 5 次放行,剩余 3
  • 静态入口已把手机号打满,新实例上 acquire(phone, 2, SMS_WINDOW) 仍拿到 true
  • 16 线程各抢 20 次、总额度 100,拿到 实际放行 100 次,没有超放
  • 300 毫秒窗口额度 2,睡过窗口后 acquire("slide", 2, window) 拿到 true

🔢 方法序号:5

🎯 方法用途:

  • 实例上的批量配额入口,一次消耗 permits 个
  • 按批扣额度的业务(一次发多条、一批扣多个令牌)用这一版

🧩 方法定义:public boolean acquire(String key, int permits, int maxRequests, Duration window)

🗣️ 入参解释:public boolean acquire(资源key, 本次配额数, 阈值, 窗口)

📤 返回值解释:boolean,额度够整笔扣掉返回 true

💡 边界口径:

  • 不足时整笔不扣,剩余数量保持原值
  • 被拒时如果队列已经被剔空,这个 key 会从表里摘掉,不留空桶
  • 校验口径与单个配额入口一致

🧪 方法示例:

  • 剩 3 个额度时 acquire(ip, 4, 8, IP_WINDOW) 拿到 false,剩余仍是 3
  • 改成要 3 个正好够,acquire(ip, 3, 8, IP_WINDOW) 拿到 true,剩余 0

🔢 方法序号:6

🎯 方法用途:

  • 查某个 key 在窗口内还剩多少配额,不消耗
  • 给前端展示「还能发几次」、做灰度阈值判断时用

🧩 方法定义:public int remaining(String key, int maxRequests, Duration window)

🗣️ 入参解释:public int remaining(资源key, 阈值, 窗口)

📤 返回值解释:int,剩余配额,不小于 0

💡 边界口径:

  • 没有限流记录的 key 直接给满额
  • 查询同样会剔除过期记录,队列被剔空时顺手把 key 摘掉
  • 传进来的 maxRequests 与 window 就是判定口径,和写入时不一致会得到另一个结果

🧪 方法示例:

  • 打满之后 remaining(phone, 2, SMS_WINDOW) 拿到 0 次
  • 从没出现过的 key remaining("13900005678", 2, SMS_WINDOW) 拿到 2 次
  • 剩 1 次时查完 remaining("x", 3, window) 拿到 2,查完占桶数仍是 1

🔢 方法序号:7

🎯 方法用途:

  • 算下一次可以放行的等待时长,直接当 Retry-After 响应头用
  • 被限流时要告诉对方等多久,别只回 429 让人盲重试

🧩 方法定义:public Duration retryAfter(String key, int maxRequests, Duration window)

🗣️ 入参解释:public Duration retryAfter(资源key, 阈值, 窗口)

📤 返回值解释:Duration,有额度给 0,满额给最早放行的等待时间

💡 边界口径:

  • 等待时长按窗口里最老那条记录算,窗口滑到它过期即可放行
  • 没记录或还有额度时给 Duration.ZERO,不是负数
  • 结果是本次调用的瞬时值,纳秒级抖动正常,给 HTTP 头记得向上取整

🧪 方法示例:

  • 额度打满后 retryAfter(phone, 2, SMS_WINDOW) 拿到 PT59.9946621S,约 59 秒
  • 300 毫秒窗口刚打满时拿到 PT0.2866983S
  • 有额度时 retryAfter("never-seen", 5, window) 拿到 PT0S

🔢 方法序号:8

🎯 方法用途:

  • 清掉单个 key 的计数,让它立刻回到满额
  • 用户完成验证码校验、申诉通过后手工放行时用

🧩 方法定义:public void reset(String key)

🗣️ 入参解释:public void reset(资源key)

📤 返回值解释:void,直接把这个 key 从桶表里移除

💡 边界口径:

  • key 为 null 不抛异常,静默什么都不做
  • 重置是整条窗口清空,不是补一个配额
  • 重置后这个 key 不再占桶,size() 会减一

🧪 方法示例:

  • reset("once-a-visitor") 之后 remaining("once-a-visitor", 1, IP_WINDOW) 拿到 1,占桶数降到 1

🔢 方法序号:9

🎯 方法用途:

  • 一次性清空全部计数,所有 key 回到满额
  • 单元测试之间复位、配置热更新后重建限流状态时用

🧩 方法定义:public void clear()

🗣️ 入参解释:public void clear()(无入参)

📤 返回值解释:void,桶表清空

💡 边界口径:

  • 只清自己这个实例,默认单例与其他实例不受影响
  • 清空不等于停服,下一波请求照样从满额开始扣
  • 没有按窗口选择性清理的版本,要精细回收用 reset(key) 或 purgeIdle

🧪 方法示例:

  • clear() 之后 size() 拿到 0,全部计数被清空

🔢 方法序号:10

🎯 方法用途:

  • 看当前有多少个 key 占着限流桶,供监控与容量观测
  • 排查「内存是不是被限流 key 吃满」时先读这个数

🧩 方法定义:public int size()

🗣️ 入参解释:public int size()(无入参)

📤 返回值解释:int,当前占桶的 key 数

💡 边界口径:

  • 只统计还留在表里的 key,窗口滑空并被触碰过的已被摘除
  • 读它不消耗配额,也不做清理
  • 数值持续只增不减,就说明该配 purgeIdle 了

🧪 方法示例:

  • 两个 key 各打一次之后 size() 拿到 2
  • clear() 之后拿到 0
  • 空闲回收之后 size() 拿到 0

🔢 方法序号:11

🎯 方法用途:

  • 清理长时间没人访问的 key,回收内存并返回被清掉的数量
  • 限流 key 是手机号、IP 这种无界集合时,交给定时任务周期调用

🧩 方法定义:public int purgeIdle(Duration idleThreshold)

🗣️ 入参解释:public int purgeIdle(空闲时长阈值)

📤 返回值解释:int,本次回收掉的 key 数

💡 边界口径:

  • 空闲时间按窗口里最后一条记录与当前时刻的差算
  • 阈值给 null、0 或负数抛 IllegalArgumentException
  • 建议阈值取窗口长度的若干倍,比窗口短会清掉还在计数的 key
  • 只登记不清理等于内存泄漏,被刷一轮就把堆吃满

🧪 方法示例:

  • 两个桶都还新鲜时 purgeIdle(Duration.ofHours(1)) 拿到 回收 0 个
  • 睡过 120 毫秒后用 50 毫秒阈值 purgeIdle(Duration.ofMillis(50)) 拿到 2 个,占桶数降到 0
  • 阈值给 Duration.ZERO 抛 idleThreshold 必须大于 0,当前为PT0S

四、工具类源码(可直接复制)

📦 源码文件

相关文章
|
云安全 安全 网络安全
|
7天前
|
JavaScript 安全 前端开发
Nodejs Brotli decoder 代码执行漏洞 (CVE-2026-111497)10629
Node.js发布安全更新修复Brotli解码器高危RCE漏洞(CVE-2026-111497),建议受影响版本用户立即升级。
2018 1
|
15天前
|
JSON 程序员 数据格式
最新版Notepad++下载、安装、配置保姆级教程(附官网安装包)
Notepad++ 是一款免费开源、轻量快速的 Windows 文本编辑器,支持多标签、语法高亮、代码折叠与宏录制,体积小、启动快、资源占用低,兼容 x64/x32/ARM 架构,是程序员和文字工作者的高效利器。
3374 0
|
14天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
2365 44
|
11天前
|
Web App开发 人工智能 前端开发
2026 年 AI 浏览器怎么选?Ego Lite、Tabbit、Chrome DevTools MCP 深度对比,开发者到底该用哪个?
2026年AI浏览器选型指南:Ego Lite专注多Agent协同(Mac优先),Tabbit国产首选,原生支持Windows与Codex控制;Chrome DevTools MCP非浏览器而是开发者利器,深度集成Console/Network/Performance调试能力,让AI真正“看懂并验证”前端代码。
1447 3
|
8天前
|
安全 应用服务中间件 Go
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
PHP2Shell 双 RCE 已遭在野利用:WordPress、Drupal 全在射程内,nginx 其实另有洞。拆成因与修复顺序
PHP2Shell 漏洞拆解:WordPress 与 nginx 为何中招
|
8天前
|
开发者 索引
DeepSeekHarness大肥鱼自带插件市场了?加个地址就能用
Open Plugin Market 是一个开放插件索引平台,收录800+优质DSH插件,支持分类浏览、智能推荐与随机发现。无需安装市场,粘贴索引地址即可一键接入,网页版直访 dshopen.org,助力小开发者被看见。(239字)
1835 1
DeepSeekHarness大肥鱼自带插件市场了?加个地址就能用
|
6天前
|
人工智能 Linux API
Magpie, 轻量本地AI网关!免费Token用不完!
Magpie 是开源AI编程模型调度网关,上线一月获1.8万Star。它通过本地高性能网关统一托管多平台API密钥、账号与免费额度,实现跨Agent的模型秒级切换、静默额度轮换与资源复用,显著降低碎片化管理成本。(239字)
Magpie, 轻量本地AI网关!免费Token用不完!
|
6天前
|
安全 程序员 应用服务中间件
PHP 被曝两大致命漏洞,请所有程序员抓紧做这件事!
PHP 官方发布了紧急安全更新,一口气修复了两个严重漏洞。如果网站和服务器用了 PHP 应该怎么处理?漏洞的原理是什么?本文给你讲清楚
720 0

热门文章

最新文章