java单机滑动窗口限流器的实现

 更新时间:2026年03月22日 14:59:59   作者:编码忘我  
本文主要介绍了java单机滑动窗口限流器的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

下面使用 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:限流
...

注意事项

  1. 内存清理:长期不活动的用户 Key 会残留队列对象,建议定时调用 cleanEmptyKeys() 清理。
  2. 并发安全:每个队列使用 synchronized 锁保证原子操作,锁粒度小,性能较高。
  3. 时间精度:使用毫秒级时间戳,滑动窗口边界判断精确。
  4. 适用场景:单机应用、对分布式一致性要求不高的限流场景。

到此这篇关于java单机滑动窗口限流器的实现的文章就介绍到这了,更多相关java 滑动窗口限流器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java解析xml之dom解析xml示例分享

    java解析xml之dom解析xml示例分享

    DOM将整个XML文件加载到内存中,并构建出节点树;应用程序可以通过遍历节点树的方式来解析XML文件中的各个节点、属性等信息; 这种方式便于对XML节点的添加修改等,而且解析也很方便,然后它比较耗费内存,解析速度也不快,下面看使用示例吧
    2014-01-01
  • 关于Spring的@Autowired依赖注入常见错误的总结

    关于Spring的@Autowired依赖注入常见错误的总结

    有时我们会使用@Autowired自动注入,同时也存在注入到集合、数组等复杂类型的场景。这都是方便写 bug 的场景,本篇文章带你了解Spring @Autowired依赖注入的坑
    2021-09-09
  • Java Swing CardLayout卡片布局的实现示例

    Java Swing CardLayout卡片布局的实现示例

    这篇文章主要介绍了Java Swing CardLayout卡片布局的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • 全面解析Java设计模式之单例模式

    全面解析Java设计模式之单例模式

    这篇文章主要帮助大家全面解析Java设计模式之单例模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-12-12
  • spring boot+redis 监听过期Key的操作方法

    spring boot+redis 监听过期Key的操作方法

    这篇文章主要介绍了spring boot+redis 监听过期Key,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • Java数据结构之数组(动力节点之Java学院整理)

    Java数据结构之数组(动力节点之Java学院整理)

    这篇文章主要介绍了Java数据结构之数组(动力节点之Java学院整理)的相关资料,包括创建和内存分配,数组封装后的使用等,需要的朋友参考下吧
    2017-04-04
  • Spring jdbc具名参数使用方法详解

    Spring jdbc具名参数使用方法详解

    这篇文章主要介绍了Spring jdbc具名参数使用方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • idea中cherry pick的用法

    idea中cherry pick的用法

    Cherry-Pick可以将一个分支的某些commit,合并到另一个分支,本文给大家分享idea中cherry pick的用法,感兴趣的朋友跟随小编一起看看吧
    2023-08-08
  • mybatis-plus使用@Delete注解批量删除实现方式

    mybatis-plus使用@Delete注解批量删除实现方式

    文章介绍了如何使用@Delete注解进行批量删除操作,包括控制器调用、接口实现和效果图展示,作者分享了个人经验,并鼓励大家参考和点赞支持
    2026-02-02
  • java 商户PC端接入支付宝支付的实现方法

    java 商户PC端接入支付宝支付的实现方法

    这篇文章主要介绍了java 商户PC端接入支付宝支付的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08

最新评论