SpringBoot基于redis zset实现滑动窗口限流

通过Redis zset实现滑动窗口限流算法

在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。限流可以认为服务降级的一种,限流通过限制请求的流量以达到保护系统的目的。

一般来说,系统的吞吐量是可以计算出一个阈值的,为了保证系统的稳定运行,一旦达到这个阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。否则,很容易导致服务器的宕机。

滑动窗口算法

滑动窗口算法思想就是记录一个滑动的时间窗口内的操作次数,操作次数超过阈值则进行限流。
在这里插入图片描述

通过zset实现滑动窗口算法思路

指定时间T内,只允许发生N次。我们可以将这个指定时间T,看成一个滑动时间窗口(定宽)。我们采用Redis的zset基本数据类型的score来圈出这个滑动时间窗口。在实际操作zset的过程中,我们只需要保留在这个滑动时间窗口以内的数据,其他的数据不处理即可。

  • 每个用户的行为采用一个zset存储,score为毫秒时间戳,value也使用毫秒时间戳(比UUID更加节省内存)
  • 只保留滑动窗口时间内的行为记录,如果zset为空,则移除zset,不再占用内存(节省内存)

SpringBoot内实现限流

效果:某个接口1分钟只允许访问n次。

我们可以通过Spring的Aop来实现解耦,通过Before通知校验接口是否达到限流阈值,如果达到直接抛异常。

限流工具类

@Slf4j
@Component
public class SlidingWindowCounter implements ApplicationContextAware {

    private static RedisTemplate<String, Object> redisTemplate;

    /**
     * 数据统计-判断数量是否超过最大限定值
     *
     * @param key        redis key
     * @param windowTime 窗口时间,单位:秒
     * @param maxNum     最大数量
     *
     * @return true-超过 false-未超过
     */
    public static boolean countOver(String key, long windowTime, long maxNum) {
        // 窗口结束时间
        long windowEndTime = System.currentTimeMillis();
        // 窗口开始时间
        long windowStartTime = windowEndTime - windowTime;

        // 按score统计key的value中的有效数量
        Long count = redisTemplate.opsForZSet().count(key, windowStartTime, windowEndTime);
        if (count == null) {
            return false;
        }
        return count > maxNum;
    }
    

    /**
     * 数据统计、数据上报同步处理,判断数量是否超过最大限定值
     *
     * @param key        redis key
     * @param windowTime 窗口时间,单位:秒
     * @param maxNum     最大数量
     *
     * @return true-超过 false-未超过
     */
    public static boolean incrementAndGet(String key, long windowTime, long maxNum) {
        // 窗口结束时间
        long windowEndTime = System.currentTimeMillis();
        // 窗口开始时间
        long windowStartTime = windowEndTime - windowTime;


        // 清除窗口过期成员
        redisTemplate.opsForZSet().removeRangeByScore(key,0,windowStartTime);

        // 按score统计key的value中的有效数量
        Long count = redisTemplate.opsForZSet().count(key, windowStartTime, windowEndTime);
        boolean limit = count!=null && count >= maxNum;

        //如果没限流,设置zset值
        if(!limit){
            // 添加当前时间 value=当前时间戳 score=当前时间戳
            redisTemplate.opsForZSet().add(key,windowStartTime,windowEndTime);
        }
        return limit;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        redisTemplate = applicationContext.getBean("redisTemplate",RedisTemplate.class);
    }
}

自定义限流注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisRateLimiter {

    /**
     * 窗口时间
     */
    long windows() default  60;

    /**
     * 窗口时间内允许访问次数
     */
    long count() default  1;

    /**
     * 时间单位
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;
}

定义AOP实现校验

@Component
@Aspect
public class RedisRateLimiterAop {

    @Autowired
    private RedissonClient redissonClient;

    @Pointcut(value = "@annotation(redisRateLimiter)")
    public void pointCut(RedisRateLimiter redisRateLimiter){}

    @Before("pointCut(redisRateLimiter)")
    public void around(JoinPoint joinPoint,RedisRateLimiter redisRateLimiter){
        String key = joinPoint.getSignature().getDeclaringTypeName()+"@"+joinPoint.getSignature().getName();

       
        long windows = redisRateLimiter.timeUnit().toMillis(redisRateLimiter.windows());
        long count = redisRateLimiter.count();
		 //我们自己实现的滑动窗口限流
        redissonTokenBucket(key, windows, count);

    }

    /**
    * 调用我们写的工具类判断是否超过阈值
    */
    private void slidingWindowLimit(String key, long windows, long count) {
        boolean  limit = SlidingWindowCounter.incrementAndGet(key, windows, count);
        if(limit){
            throw new RuntimeException("限流");
        }
    }
    /**
	* 如果使用了Redisson,可以直接使用令牌桶来实现限流
	*/
    private void redissonTokenBucket(String key, long windows, long count) {
        // 1、 声明一个限流器
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        // 2、 设置速率,5秒中产生3个令牌
        rateLimiter.trySetRate(RateType.OVERALL, count, windows, RateIntervalUnit.MILLISECONDS);

        // 3、试图获取一个令牌,获取到返回true
        boolean hasToken = rateLimiter.tryAcquire(1);
        if(!hasToken){
            throw new RuntimeException("限流");
        }
    }
}

使用自定义注解实现限流

@RestController
public class TestController {

    @RequestMapping("/test")
    @RedisRateLimiter(windows = 5,count = 1,timeUnit = TimeUnit.SECONDS)
    public String test(){
        return "success";
    }
}

Redisson提供的令牌桶工具类

redisson内提供了RateLimiter工具类,我们可以通过RateLimiter定义令牌桶来实现现限流。

代码和上面的aop方法类似,只需要将 slidingWindowLimit 方法替换为 redissonTokenBucket方法即可。

private void redissonTokenBucket(String key, long windows, long count) {
    // 1、 声明一个限流器
    RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
    // 2、 设置速率,5秒中产生3个令牌
    rateLimiter.trySetRate(RateType.OVERALL, count, windows, RateIntervalUnit.MILLISECONDS);

    // 3、试图获取一个令牌,获取到返回true
    boolean hasToken = rateLimiter.tryAcquire(1);
    if(!hasToken){
        throw new RuntimeException("限流");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/556671.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【leetcode面试经典150题】59. 合并两个有序链表(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

【Java框架】Spring框架(四)——Spring中的Bean的创建与生命周期

目录 SpringBean的创建步骤后置处理器(PostProcessor)BeanFactoryPostProcessorBeanPostProcessorInstantiationAwareBeanPostProcessorpostProcessBeforeInstantiationpostProcessAfterInstantiationpostProcessProperties SmartInstantiationAwareBeanPostProcessordetermine…

空心电抗器的matlab建模与性能仿真分析

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 空心电抗器是一种无铁芯的电感元件&#xff0c;主要由一圈或多圈导线绕制在非磁性材料制成的空心圆筒或其他形状的骨架上构成。其工作原理基于法拉第电磁感应定律&#xff0c;…

Maui 开始笔记

1&#xff0c;仿真器硬件加速&#xff0c;需要安装 2&#xff0c;刚创建的maui 不添加的话&#xff0c;启动可能时会自动退出&#xff0c;不退出&#xff0c;可以不加次配置 MauiApp1.csproj 文件中配置 在 PropertyGroup 元素下添加 <WindowsAppSdkDeploymentManagerIniti…

【Qt】常用控件(LCD Number/进度条/日历)

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、LCD Number(LCD显示器) 一个倒计时程序 二、ProgressBar(进度条) 1、创建一个进度条&#xff0c;100ms进度增加…

hv第一坑:定时器

错误代码 重试策略&#xff1a;一次延迟1s,最长30s直至事件成功。 int try_count 0;//do something if(not success)m_loop->setTimerInLoop((try_count > 30 ? 30: try_count) *1000 , cb, INFINITE, 0x100);表现现象 cpu 爆了内存爆了 总结原因 hv内部代码bug&…

C语言—常用字符串函数剖析

字符串函数 cplusplus.com/reference/cstring/ 更多没有总结到的函数大家可以自行查阅 这篇文章只是把最需要知道的函数做一个总结 strlen size_t strlen ( const char * str );字符串已经 ‘\0’ 作为结束标志&#xff0c;strlen函数返回的是在字符串中 ‘\0’ 前面出现的…

力扣面试150 文本左右对齐 贪心 字符串 满注释版

Problem: 68. 文本左右对齐 思路 &#x1f469;‍&#x1f3eb; 三叶题解 &#x1f496; Code class Solution { public List<String> fullJustify(String[] words, int maxWidth){List<String> ans new ArrayList<>();// 结果List<String> list …

【网络安全技术】——网络安全设备(学习笔记)

&#x1f4d6; 前言&#xff1a;网络防火墙&#xff08;简称为“防火墙”&#xff09;是计算机网络安全管理中应用最早和技术发展最快的安全产品之一。随着互联应用的迅猛发展&#xff0c;各种安全问题和安全隐患日渐突出。防火墙及相关安全技术能够最大可能地解决各类安全问题…

SpringBoot整合零一万物模型API进行多轮对话

前期准备工作 零一万物官网&#xff1a;https://www.01.ai/cn 零一万物大模型开放平台&#xff1a;https://platform.lingyiwanwu.com/ 选择理由 性价比高很高&#xff0c;模型整体不错&#xff0c;新用户送60元&#xff0c;非常适合研究学习。 开发 只提供了http接口和p…

测试JAVA 测开

测试、java测开 1、测试用例要素&#xff08;4个重要要素&#xff09;2、测试用例的好处3、测试用例的设计方法3.1 基于需求设计测试用例3.2 等价类3.3 边界值3.4 判定表 1、测试用例要素&#xff08;4个重要要素&#xff09; 测试环境操作步骤测试数据预期结果 2、测试用例的…

计算机网络:CSMA/CA协议

计算机网络&#xff1a;CSMA/CA协议 CSMA/CA概述帧间间隔工作原理退避算法虚拟载波监听 CSMA/CA概述 讲解CSMA/CA之前&#xff0c;我们回顾一下CSMA/CD的三个特性&#xff1a; 多址接入MA&#xff1a;多个主机连接在一条总线上&#xff0c;竞争使用总线 载波监听CS&#xff1a…

2025考研数学武忠祥强化班视频,百度网盘课程+讲义PDF更新

25考研的小伙伴们现在应该基础都学习的差不多了吧&#xff01; 是时候进入强化阶段的学习啦。 2025考研数学强化班全程网盘&#xff1a;https://pan.baidu.com/s/1Z029fuCLkyyhIRFqd5QKcg 提取码&#xff1a;p3ue 晚上好&#xff0c;聊聊17堂课的看课攻略。 今年的17堂课还…

文件解读 | 工信部88号文发布,强调7大任务、6大重点!

工业和信息化部于4月7日印发通知&#xff0c;要求各有关单位按照安全生产治本攻坚三年行动工作部署要求&#xff0c;坚持安全发展、预防为主、技管结合&#xff0c;把安全生产和网络运行安全的任务、措施、责任真正落到实处&#xff0c;切实筑牢保障人民群众生命财产安全和社会…

【大模型书籍PDF】复旦新出!大规模语言模型:从理论到实践(书籍分享)

自2018年以来&#xff0c;包含Google、OpenAI、Meta、百度、华为等公司和研究机构都纷纷发布了包括BERT&#xff0c; GPT等在内多种模型&#xff0c;并在几乎所有自然语言处理任务中都表现出色。 今天给大家推荐一本大模型方面的书籍<大规模语言模型&#xff1a;从理论到实…

2024上海国际特种电子暨军民两用技术展览会

2024上海国际特种电子暨军民两用技术展览会 2024 Shanghai International Special Electronics and Military Civilian Dual Use Technology Exhibition 时间&#xff1a;2024年11月18日-20日 地点&#xff1a;上海新国际博览中心 详询主办方陆先生 I38&#xff08;前三位…

模拟相机拍照——对文档进行数据增强

一. 背景 假如我们有一个标准文件&#xff0c;我们对其进行文字识别、版面分析或者其他下游任务就比较容易。然而&#xff0c;当图片是手机拍照获取的&#xff0c;图片中往往有阴影、摩尔纹、弯曲。 那么&#xff0c;如何通过标准的文档&#xff0c;获得类似相机拍照的图片呢&…

Java 网络编程之TCP:基于BIO

环境&#xff1a; jdk 17 IntelliJ IDEA 2023.1.1 (Ultimate Edition) Windows 10 专业版 22H2 TCP&#xff1a;面向连接的&#xff0c;可靠的数据传送协议 Java中的TCP网络编程&#xff0c;其实就是基于常用的BIO和NIO来实现的&#xff0c;本文先讨论BIO&#xff1b; BIO…