Java 实现滑动时间窗口限流算法的代码

 更新时间:2020年11月26日 11:10:44   投稿:mrr  
这篇文章主要介绍了Java 实现滑动时间窗口限流算法的代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

在网上搜滑动时间窗口限流算法,大多都太复杂了,本人实现了个简单的,先上代码:

package cn.dijia478.util;

import java.time.LocalTime;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 滑动时间窗口限流工具
 * 本限流工具只适用于单机版,如果想要做全局限流,可以按本程序的思想,用redis的List结构去实现
 *
 * @author dijia478
 * @date 2020-10-13 10:53
 */
public class SlideWindow {

  /** 队列id和队列的映射关系,队列里面存储的是每一次通过时候的时间戳,这样可以使得程序里有多个限流队列 */
  private volatile static Map<String, List<Long>> MAP = new ConcurrentHashMap<>();

  private SlideWindow() {}

  public static void main(String[] args) throws InterruptedException {
    while (true) {
      // 任意10秒内,只允许2次通过
      System.out.println(LocalTime.now().toString() + SlideWindow.isGo("ListId", 2, 10000L));
      // 睡眠0-10秒
      Thread.sleep(1000 * new Random().nextInt(10));
    }
  }

  /**
   * 滑动时间窗口限流算法
   * 在指定时间窗口,指定限制次数内,是否允许通过
   *
   * @param listId   队列id
   * @param count   限制次数
   * @param timeWindow 时间窗口大小
   * @return 是否允许通过
   */
  public static synchronized boolean isGo(String listId, int count, long timeWindow) {
    // 获取当前时间
    long nowTime = System.currentTimeMillis();
    // 根据队列id,取出对应的限流队列,若没有则创建
    List<Long> list = MAP.computeIfAbsent(listId, k -> new LinkedList<>());
    // 如果队列还没满,则允许通过,并添加当前时间戳到队列开始位置
    if (list.size() < count) {
      list.add(0, nowTime);
      return true;
    }

    // 队列已满(达到限制次数),则获取队列中最早添加的时间戳
    Long farTime = list.get(count - 1);
    // 用当前时间戳 减去 最早添加的时间戳
    if (nowTime - farTime <= timeWindow) {
      // 若结果小于等于timeWindow,则说明在timeWindow内,通过的次数大于count
      // 不允许通过
      return false;
    } else {
      // 若结果大于timeWindow,则说明在timeWindow内,通过的次数小于等于count
      // 允许通过,并删除最早添加的时间戳,将当前时间添加到队列开始位置
      list.remove(count - 1);
      list.add(0, nowTime);
      return true;
    }
  }

}

运行可以看到,任意10秒内,通过的次数不超过2次。或者按照实现原理来说,任意通过2次内的时间差,都不超过10秒:

这里画图做说明,为什么这样可以做到滑动窗口限流,假设10秒内允许通过5次

1.这条线就是队列list,当第一个事件进来,队列大小是0,时间是第1秒:

2.因为size=0,小于5,都没有到限制的次数,完全不用考虑时间窗口,直接把这次事件的时间戳放到0的位置:

3.第2.8秒的时候,第二个事件来了。因为此时size=1,还是小于5,把这次事件的时间戳放到0的位置,原来第1秒来的事件时间戳会往后移动一格:

4.陆续的又来了3个事件,队列大小变成了5,先来的时间戳依次向后移动。此时,第6个事件来了,时间是第8秒:

5.因为size=5,不小于5,此时已经达到限制次数,以后都需要考虑时间窗口了。所以取出位置4的时间(离现在最远的时间),和第6个事件的时间戳做比较:

6.得到的差是7秒,小于时间窗口10秒,说明在10秒内,来的事件个数大于5了,所以本次不允许通过:

7.接下来即便来上100个事件,只要时间差小于等于10秒,都同上,拒绝通过:

8.第11.1秒,第101次事件过来了。因为size=5,不小于5,所以取出位置4的时间(离现在最远的时间),和第101个事件的时间戳做比较:

9.得到的差是10.1秒,大于时间窗口10秒,说明在10秒内,来的事件个数小于等于5了,所以本次允许通过:

10.删除位置4的时间(离现在最远的时间),把这次事件的时间戳放到0的位置,后面的时间戳依次向后移动:

往后再来其他事件,就是重复4-10的步骤,即可实现,在任意滑动时间窗口内,限制通过的次数

其本质思想是转换概念,将原本问题的确定时间大小,进行次数限制。转换成确定次数大小,进行时间限制。

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

相关文章

  • Java实现冒泡排序算法及对其的简单优化示例

    Java实现冒泡排序算法及对其的简单优化示例

    这篇文章主要介绍了Java实现冒泡排序算法及对其的简单优化示例,冒泡排序的最差时间复杂度为O(n^2),最优时间复杂度为O(n),存在优化的余地,需要的朋友可以参考下
    2016-05-05
  • Eureka源码解析服务离线状态变更

    Eureka源码解析服务离线状态变更

    这篇文章主要为大家介绍了Eureka源码解析服务离线的状态变更示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • SpringBoot临时属性设置方法

    SpringBoot临时属性设置方法

    这篇文章主要介绍了SpringBoot临时属性设置方法,SpringBoot工程可以基于java环境独立进行jar文件启动服务,文中给大家提到了命令行启动常见问题以及解决方案,需要的朋友可以参考下
    2022-09-09
  • Java使用Jasypt进行加密和解密的技术指南

    Java使用Jasypt进行加密和解密的技术指南

    Jasypt (Java Simplified Encryption) 是一个简化 Java 应用中加密工作的库,它支持加密和解密操作,易于与 Spring Boot 集成,通过 Jasypt,可以安全地管理敏感信息,比如数据库密码、API 密钥等,本文介绍了Java使用Jasypt进行加密和解密的技术指南,需要的朋友可以参考下
    2025-03-03
  • 深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

    深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

    文章系统讲解了Spring AOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能优化、常见陷阱及解决方案,并对比了SpringAOP与AspectJ的编译时织入特性,强调合理应用场景与避免滥用的重要性,感兴趣的朋友一起看看吧
    2025-06-06
  • Java线程取消的三种方式

    Java线程取消的三种方式

    文章介绍了 Java 线程取消的 3 种方式,不推荐使用 stop 方法和 volatile 设标记位停止线程,线程中断机制是协作式的,一个线程请求中断,另一线程响应,线程可检查自身中断状态或捕获 InterruptedException 来合适处理以响应中断,确保安全有序停止,避免资源泄露等问题
    2024-12-12
  • Java单链表的实现代码

    Java单链表的实现代码

    这篇文章主要介绍了Java单链表的实现代码的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-07-07
  • java线程池合理设置最大线程数和核心线程数方式

    java线程池合理设置最大线程数和核心线程数方式

    这篇文章主要介绍了java线程池合理设置最大线程数和核心线程数方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 浅谈一下Java线程组ThreadGroup

    浅谈一下Java线程组ThreadGroup

    ThreadGroup是为了方便线程管理出现了,可以统一设定线程组的一些属性,比如setDaemon,设置未处理异常的处理方法,设置统一的安全策略等等,需要的朋友可以参考下
    2023-05-05
  • java高级用法之注解和反射讲义

    java高级用法之注解和反射讲义

    这篇文章主要给大家介绍了关于java高级用法之注解和反射讲义的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05

最新评论