下面使用 concurrenthashmap + concurrentlinkeddeque 实现一个滑动窗口限流器,限制在 10 秒内最多 n 次请求(例如 5 次)。这种方案只依赖本地内存,适合单机限流场景。
实现原理
为每个用户(或 ip)维护一个 双端队列,存储每次请求的时间戳(毫秒)。
每次请求时,先清理队列中 超过 10 秒 的旧时间戳,再判断队列大小是否小于阈值:
- 若小于,则添加当前时间戳,返回 允许;
- 否则,返回 限流。
使用
concurrenthashmap保证对用户 key 的线程安全,使用synchronized对每个队列做同步,避免并发修改。
代码实现
import java.util.map;
import java.util.concurrent.concurrenthashmap;
import java.util.concurrent.concurrentlinkeddeque;
import java.util.concurrent.timeunit;
/**
* 基于 map 的滑动窗口限流器(10秒限频)
*/
public class localslidingwindowratelimiter {
// 存储每个用户的请求时间戳队列
private final map<string, concurrentlinkeddeque<long>> userqueuemap = new concurrenthashmap<>();
private final int windowseconds; // 窗口大小(秒)
private final int maxrequests; // 窗口内最大请求数
public localslidingwindowratelimiter(int windowseconds, int maxrequests) {
this.windowseconds = windowseconds;
this.maxrequests = maxrequests;
}
/**
* 检查是否允许请求
* @param key 限流标识(如 userid、ip)
* @return true 允许,false 被限流
*/
public boolean allowrequest(string key) {
long now = system.currenttimemillis();
long windowstart = now - timeunit.seconds.tomillis(windowseconds);
// 获取或创建该用户的队列
concurrentlinkeddeque<long> deque = userqueuemap.computeifabsent(key,
k -> new concurrentlinkeddeque<>());
// 对队列加锁,保证清理和添加的原子性
synchronized (deque) {
// 1. 清理过期记录(窗口外的旧时间戳)
while (!deque.isempty() && deque.peekfirst() < windowstart) {
deque.pollfirst();
}
// 2. 判断是否超过阈值
if (deque.size() < maxrequests) {
// 允许请求,记录当前时间戳
deque.addlast(now);
return true;
} else {
return false;
}
}
}
// 可选:定期清理无数据的用户 key,避免内存泄漏(可以用 scheduledexecutorservice 定时清理)
public void cleanemptykeys() {
userqueuemap.entryset().removeif(entry -> entry.getvalue().isempty());
}
}使用示例
public class main {
public static void main(string[] args) throws interruptedexception {
// 限流规则:10秒内最多5次请求
localslidingwindowratelimiter limiter = new localslidingwindowratelimiter(10, 5);
string userid = "user_123";
for (int i = 0; i < 10; i++) {
boolean allowed = limiter.allowrequest(userid);
system.out.println("请求 " + (i + 1) + ":" + (allowed ? "通过" : "限流"));
thread.sleep(1000); // 模拟间隔 1 秒
}
}
}输出示例(第 6 次请求被限流,因为 10 秒内已有 5 次):
请求 1:通过
请求 2:通过
请求 3:通过
请求 4:通过
请求 5:通过
请求 6:限流
请求 7:限流
...
注意事项
- 内存清理:长期不活动的用户 key 会残留队列对象,建议定时调用
cleanemptykeys()清理。 - 并发安全:每个队列使用
synchronized锁保证原子操作,锁粒度小,性能较高。 - 时间精度:使用毫秒级时间戳,滑动窗口边界判断精确。
- 适用场景:单机应用、对分布式一致性要求不高的限流场景。
到此这篇关于java单机滑动窗口限流器的实现的文章就介绍到这了,更多相关java 滑动窗口限流器内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论