由于最近有一个统计单位时间内某key的访问次数的需求,譬如每5秒访问了redis的某key超过100次,就取出该key单独处理。

这样的单位时间统计,很明显我们都知道有个边界问题,譬如5秒内100次的限制。刚好前4.99秒访问都是0,最后0.01秒来了100次,5.01秒又来了100次。也就是访问有明显的毛刺情况出现,为了弱化这个毛刺情况,我们可以采用滑动窗口。

滑动窗口

滑动窗口的主要原理比较简单,就是将这个单位时间进行拆分,譬如5秒的统计范围,我们将它划分成5个1秒。

当请求进来时,先判断当前请求属于这5个1秒的时间片中的哪个,然后将对应的时间片对应的统计值加1,再判断当前加上前4个时间片的次数总和是否已经超过了设置的阈值。

当时间已经到达第6个时间片时,就把第一个时间片给干掉,因为无论第一片是多少个统计值,它都不会再参与后续的计算了。

就这样,随着时间的推移,统计值就随着各个时间片的滚动,不断地进行统计。

具体要将单位时间拆分为多少片,要根据实际情况来决定。当然,毫无疑问的是切分的越小,毛刺现象也越少。系统统计也越准确,随之就是内存占用会越大,因为你的这个窗口的数组会更大。

代码实现思路就是定义好分片数量,每个分片都有一个独立的计数器,所有的分片合计为一个数组。当请求来时,按照分片规则,判断请求应该划分到哪个分片中去。要判断是否超过阈值,就将前N个统计值相加,对比定义的阈值即可。

代码我直接引用别人写好的了,源代码在https://www.iteye.com/blog/go12345-1744728


import java.util.concurrent.atomic.AtomicInteger;/*** 滑动窗口。该窗口同样的key,都是单线程计算。** @author wuweifeng wrote on 2019-12-04.*/
public class SlidingWindow {/*** 循环队列,就是装多个窗口用,该数量是windowSize的2倍*/private AtomicInteger[] timeSlices;/*** 队列的总长度*/private int timeSliceSize;/*** 每个时间片的时长,以毫秒为单位*/private int timeMillisPerSlice;/*** 共有多少个时间片(即窗口长度)*/private int windowSize;/*** 在一个完整窗口期内允许通过的最大阈值*/private int threshold;/*** 该滑窗的起始创建时间,也就是第一个数据*/private long beginTimestamp;/*** 最后一个数据的时间戳*/private long lastAddTimestamp;public static void main(String[] args) {//1秒一个时间片,窗口共5个SlidingWindow window = new SlidingWindow(100, 4, 8);for (int i = 0; i < 100; i++) {System.out.println(window.addCount(2));window.print();System.out.println("--------------------------");try {Thread.sleep(102);} catch (InterruptedException e) {e.printStackTrace();}}}public SlidingWindow(int duration, int threshold) {//超过10分钟的按10分钟if (duration > 600) {duration = 600;}//要求5秒内探测出来的,if (duration <= 5) {this.windowSize = 5;this.timeMillisPerSlice = duration * 200;} else {this.windowSize = 10;this.timeMillisPerSlice = duration * 100;}this.threshold = threshold;// 保证存储在至少两个windowthis.timeSliceSize = windowSize * 2;reset();}public SlidingWindow(int timeMillisPerSlice, int windowSize, int threshold) {this.timeMillisPerSlice = timeMillisPerSlice;this.windowSize = windowSize;this.threshold = threshold;// 保证存储在至少两个windowthis.timeSliceSize = windowSize * 2;reset();}/*** 初始化*/private void reset() {beginTimestamp = SystemClock.now();//窗口个数AtomicInteger[] localTimeSlices = new AtomicInteger[timeSliceSize];for (int i = 0; i < timeSliceSize; i++) {localTimeSlices[i] = new AtomicInteger(0);}timeSlices = localTimeSlices;}private void print() {for (AtomicInteger integer : timeSlices) {System.out.print(integer + "-");}}/*** 计算当前所在的时间片的位置*/private int locationIndex() {long now = SystemClock.now();//如果当前的key已经超出一整个时间片了,那么就直接初始化就行了,不用去计算了if (now - lastAddTimestamp > timeMillisPerSlice * windowSize) {reset();}return (int) (((now - beginTimestamp) / timeMillisPerSlice) % timeSliceSize);}/*** 增加count个数量*/public boolean addCount(int count) {//当前自己所在的位置,是哪个小时间窗int index = locationIndex();
//        System.out.println("index:" + index);//然后清空自己前面windowSize到2*windowSize之间的数据格的数据//譬如1秒分4个窗口,那么数组共计8个窗口//当前index为5时,就清空6、7、8、1。然后把2、3、4、5的加起来就是该窗口内的总和clearFromIndex(index);int sum = 0;// 在当前时间片里继续+1sum += timeSlices[index].addAndGet(count);//加上前面几个时间片for (int i = 1; i < windowSize; i++) {sum += timeSlices[(index - i + timeSliceSize) % timeSliceSize].get();}System.out.println(sum + "---" + threshold);lastAddTimestamp = SystemClock.now();return sum >= threshold;}private void clearFromIndex(int index) {for (int i = 1; i <= windowSize; i++) {int j = index + i;if (j >= windowSize * 2) {j -= windowSize * 2;}timeSlices[j].set(0);}}}
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;/*** 用于解决高并发下System.currentTimeMillis卡顿* @author lry*/
public class SystemClock {private final int period;private final AtomicLong now;private static class InstanceHolder {private static final SystemClock INSTANCE = new SystemClock(1);}private SystemClock(int period) {this.period = period;this.now = new AtomicLong(System.currentTimeMillis());scheduleClockUpdating();}private static SystemClock instance() {return InstanceHolder.INSTANCE;}private void scheduleClockUpdating() {ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {@Overridepublic Thread newThread(Runnable runnable) {Thread thread = new Thread(runnable, "System Clock");thread.setDaemon(true);return thread;}});scheduler.scheduleAtFixedRate(() -> now.set(System.currentTimeMillis()), period, period, TimeUnit.MILLISECONDS);}private long currentTimeMillis() {return now.get();}/*** 用来替换原来的System.currentTimeMillis()*/public static long now() {return instance().currentTimeMillis();}
}

参照代码main方法,通过修改每个时间片的时间,窗口数量,阈值,来进行测试。

这就是简单实现了。

Java简单实现滑动窗口相关推荐

  1. 剑指offer编程试题Java实现--64.滑动窗口的最大值

    个人博客:小景哥哥 64.滑动窗口的最大值 题目描述 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值.例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在 ...

  2. 滑动窗口算法框架(Java版)秒杀力扣题(76、567、438、3、485)

    一.声明 1.非常感谢东哥(labuladong)分享了**滑动窗口算法框架**: 2.我在理解了东哥的思想后,用Java实现了滑动窗口算法框架,一来方便自己学习,二来方便一些Java小伙伴: 3.再 ...

  3. Java实现滑动窗口算法

    Java实现滑动窗口算法 滑动窗口算法 滑动窗口应用实例 基本实例 断路器怎么识别断路器的状态 代码实现 滑动窗口算法 思想不滑坡 方法总比困难多 滑动窗口协议(Sliding Window Prot ...

  4. 滑动窗口算法通用思想

    文章目录 一.最小覆盖子串 二.找到字符串中所有字母异位词 三.无重复字符的最长子串 最后总结 本文详解「滑动窗口」这种高级双指针技巧的算法框架,带你秒杀几道难度较大的子字符串匹配问题: 最小覆盖子串 ...

  5. 【Leetcode数组--子数组--滑动窗口】209. 长度最小的子数组 904. 水果成篮 1004. 最大连续1的个数 III 76. 最小覆盖子串(有数组操作中重要的方法:滑动窗口!!!!)

    文章目录 Leetcode209 1.问题描述 2.解决方案 解法一:两个错误思路的算法 解法二:暴力 解法三:滑动窗口法(O(n)) Leetcode904 1.问题描述 2.解决方案 Leetco ...

  6. OpenCV--实现图像滑动窗口截取子图操作

    功能:利用opencv实现图像滑动窗口操作(即利用已知尺寸的窗口遍历整幅图像,形成许多子图像) vs2013+opencv3.1 2016.10 函数实现: slidingWnd.h #ifndef ...

  7. 滑动窗口(最大最小值)的经典例题

    滑动窗口简单概念 滑动窗口是我们假想出的一种数据结构,我们在这篇文章实现的窗口,能较快速的求区间最大最小值 在一些区间不回退的题目中运行效率也十分优秀 设窗口的左边界为l,右边界为r,(规定l< ...

  8. Java熔断框架有哪些_降级熔断框架 Hystrix 源码解析:滑动窗口统计

    降级熔断框架 Hystrix 源码解析:滑动窗口统计 概述 Hystrix 是一个开源的降级熔断框架,用于提高服务可靠性,适用于依赖大量外部服务的业务系统.什么是降级熔断呢? 降级 业务降级,是指牺牲 ...

  9. 简单介绍4种限流算法!(固定窗口计数器算法、滑动窗口计数器算法、漏桶算法、令牌桶算法)...

    作者:架构小菜 链接:https://www.jianshu.com/p/7987bf427b5b 简单介绍 4 种非常好理解并且容易实现的限流算法! 一.固定窗口计数器算法 规定我们单位时间处理的请 ...

最新文章

  1. 马云成功靠的是机遇还是努力?网友戏谑:是那张其貌不扬的脸
  2. apache2.2.21 + php5.3.8 + mysql5.5配置
  3. 分享按钮 Social Buttons for Bootstrap
  4. (30)FPGA米勒型状态机设计(一段式)(第6天)
  5. 腾讯广告算法大赛 | 第一周周冠军心得分享
  6. 京东被曝显卡售后不肯维修要原价退款;​IBM发布第一个2纳米芯片;Bootstrap 5.0.0发布|极客头条...
  7. Three20 NetWork
  8. 实力吊打国家黑客:从密码喷洒到完全控制网络只需几天
  9. 速查 Git 常用命令
  10. 【技术】Java打印菱形
  11. Ubuntu14.04+CUDA6.5+OpenCV2.4+Caffee配置
  12. 软件工程中的十三种文档
  13. 关于『区位码』、『国标码』、『机内码』的转换问题
  14. Intellij IDEA破解激活
  15. 给网页加一个全屏转场动画 HTML JS
  16. C++ 图书馆管理系统
  17. Win10下蓝牙音箱无法调节音量的解决方案
  18. java中IOException是什么异常
  19. MyBatis Plus Generator 代码生成器 v3.5.x 案例,含校验、MapStruct、Swagger、QO、VO,自定义 FreeMarker 模板引擎
  20. Scrapy 豆瓣搜索页爬虫

热门文章

  1. 《测绘管理与法律法规》——测绘项目组织实施
  2. matlab祛除海温全球变暖趋势,基于多模式数值试验揭示海洋在21世纪初全球陆地变暖减缓中的作用...
  3. 代码托管工具-Git/tortoise,develop与master的推送概念、日志找回以及小乌龟tortoise的简单使用
  4. numpy库的flatten()ravel()
  5. Windows XP 基本操作
  6. 一文彻底搞懂leveldb架构
  7. 系统消息 -- 键鼠消息
  8. gcc: unlikely, likely
  9. 保险小常识:索赔提出制和事故发生制
  10. 考研数学英语第一轮复习方法