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

该博客介绍了一个基于Java实现的滑动窗口限流工具类,支持单机场景下的接口防刷、验证码频率控制与第三方配额保护。工具采用System.nanoTime()单调时钟,规避系统校时与时间回拨导致的限流失效问题。提供11个核心方法,涵盖配额获取、批量消耗、剩余查询、等待时长计算、手动重置、内存清理等能力,适用于JDK 17+环境。

📌 人工智能: 基于SpringAI的Java智能应用开发:大模型集成与RAG系统设计

📋 目录

一、工具类功能

按「资源 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

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

📦 源码文件

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

资源创作小能手

若对您有所帮助,请点点关注哟~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值