SpringBoot项目实现自定义注解方式的接口限流

news2025/1/21 5:54:58

一,实现原理

该限流方式使用的是令牌桶算法,令牌桶算法是基于漏桶算法的一种改进,主要在于令牌桶算法能够在限制服务调用的平均速率的同时,还能够允许一定程度内的突发调用。

  1. 系统以固定的速率向桶中添加令牌
  2. 当有请求到来时,会尝试从桶中移除一个令牌,如果桶中有足够的令牌,则请求可以被处理或数据包可以被发送;
  3. 如果桶中没有令牌,那么请求将被拒绝;
  4. 桶中的令牌数不能超过桶的容量,如果新生成的令牌超过了桶的容量,那么新的令牌会被丢弃。
  5. 令牌桶算法的一个重要特性是,它能够应对突发流量。当桶中有足够的令牌时,可以一次性处理多个请求,这对于需要处理突发流量的应用场景非常有用。但是又不会无限制的增加处理速率导致压垮服务器,因为桶内令牌数量是有限制的。

如图所示:
在这里插入图片描述

二,代码实现

Guava中的RateLimiter就是基于令牌桶实现的,可以直接拿来使用。
1. 引入依赖

		<!--aop切面依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>33.1.0-jre</version>
        </dependency>

2. 创建注解

/**
 * 限流注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {

    /**
     * 限流key
     */
    String key() default "";

    /**
     * 限流时间,单位秒
     */
    int time() default 1;

    /**
     * 限流次数
     */
    int count() default 2;

    /**
     * 时间类型
     */
    TimeUnit timeUnit() default TimeUnit.SECONDS;

    /**
     * 提示消息
     */
    String message() default "系统繁忙,请稍后重试";
}

3. AOP切面实现

该切面实现只对接口进行进行限流。

/**
 * 限流切面处理
 */
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {

    private final Map<String, RateLimiter> rateLimiterMap = Maps.newConcurrentMap();

    /**
     * 切面方法,注解之前执行
     */
    @Around("@annotation(rateLimit)")
    public Object doBefore(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        // key的作用,不同的接口,不同的限流控制
        String key = rateLimit.key();
        RateLimiter rateLimiter;
        if (!rateLimiterMap.containsKey(key)){
            // 创建令牌桶,设置每秒发送得令牌
            rateLimiter = RateLimiter.create(rateLimit.count());
            rateLimiterMap.put(key, rateLimiter);
            log.info("新建了令牌桶={},容量={}", key, rateLimit.count());
        }
        rateLimiter = rateLimiterMap.get(key);
        // 获取令牌,在规定的时间获取令牌,获取不到返回false
        boolean acquire = rateLimiter.tryAcquire(rateLimit.time(), rateLimit.timeUnit());
        // 拿不到令牌,直接返回异常信息
        if (!acquire){
            log.error("令牌桶={},获取令牌失败", key);
            throw new RuntimeException(rateLimit.message());
        }
        return point.proceed();
    }

根据接口参数来区分接口限流的切面实现,参数可以是用户id或者ip地址,这样就实现了具体用户或ip限流

/**
 * 限流切面处理
 */
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {

    /**
     * 方法参数解析器
     */
    private final ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();


    private final Map<String, RateLimiter> rateLimiterMap = Maps.newConcurrentMap();

    /**
     * 切面方法,注解之前执行
     */
    @Around("@annotation(rateLimit)")
    public Object doBefore(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        // 获取方法(通过方法签名来获取)
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        // 获取参数值
        Object[] args = point.getArgs();
        // 获取方法上参数的名称
        String[] parameterNames = pnd.getParameterNames(method);
        String parameter = "";
        for (int i = 0; i < parameterNames.length; i++) {
            String parameterName = parameterNames[i];
            if (parameterName.equals("param")){
                parameter = (String) args[i];
            }
        }
        // key的作用,不同的接口,不同的流量控制
        String key = rateLimit.key() + parameter ;
        RateLimiter rateLimiter;
        if (!rateLimiterMap.containsKey(key)){
            // 创建令牌桶,设置每秒发送得令牌
            rateLimiter = RateLimiter.create(rateLimit.count());
            rateLimiterMap.put(key, rateLimiter);
            log.info("新建了令牌桶={},容量={}", key, rateLimit.count());
        }
        rateLimiter = rateLimiterMap.get(key);
        // 获取令牌,在规定的时间获取令牌,获取不到返回false
        boolean acquire = rateLimiter.tryAcquire(rateLimit.time(), rateLimit.timeUnit());
        // 拿不到令牌,直接返回异常信息
        if (!acquire){
            log.error("令牌桶={},获取令牌失败", key);
            throw new RuntimeException(rateLimit.message());
        }
        return point.proceed();
    }

3. 注解应用

    @RateLimit(key = "index", count = 5)
    @GetMapping("/index/{param}")
    public String index(@PathVariable("param") String param){
        return param + "hello world!";
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序实现图生图(AI动漫特效)效果代码(触站API)

1.效果 触站AI图生图 2.本次用的是触站平台的API,我申请的适用积分,有水印(博主没钱)。如果需要没有水印的可以去买他们的资源包 3.首先我们需要去触站官网平台注册/登录账号(已注册可跳过该步骤) 4.开通API权限 我们可以在主页看到自己免费获取的500积分,用于接口调用…

SHA-3算法:新一代的哈希函数标准

在信息安全领域&#xff0c;哈希函数是不可或缺的工具之一&#xff0c;它能够将任意长度的数据转换为固定长度的字符串&#xff0c;通常用于数据完整性验证、数字签名、密码存储等场景。随着计算能力的提升和攻击技术的发展&#xff0c;原有的哈希算法如SHA-1和SHA-2系列逐渐显…

数据结构之归并排序算法【图文详解】

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;LiUEEEEE                        …

金属切削机床5G智能工厂工业物联数字孪生,推进制造业数字化转型

金属切削机床5G智能工厂工业物联数字孪生&#xff0c;推进制造业数字化转型。随着工业4.0时代的到来&#xff0c;制造业正面临着前所未有的变革与挑战。在这场变革中&#xff0c;金属切削机床智能工厂工业物联数字孪生平台正成为推动制造业数字化转型的重要力量。 数字孪生是指…

Ego微商项目部署(小程序项目)(全网最详细教程)

1.项目部署前的准备 1.1获取APPID和APPSecret&#xff08;微信小程序&#xff09; 微信小程序注册流程及APPID&#xff0c;APPSecret获取-CSDN博客 把获取到的APPID&#xff0c;APPSecret复制粘贴&#xff0c;保存下来&#xff0c;等会要用到 1.2测试工具 navicat&#xf…

ps怎么设置立体字

一. 输入文字并调整大小和位置。 二. 使用CtrlAlt和T键复制并自由变换文字图层。 三. 通过键盘的方向键将复制的图层向左、向上各移动一个像素&#xff0c;多次重复操作以创建立体效果。 更多实用教程: 点击进入

PS系统教程11

HUD拾色器 作用&#xff1a;它可以帮助使用者更加高效地选择和使用颜色&#xff0c;从而提高工作效率和设计质量。 先确定色相值改变饱和度改变亮度使用HUD拾色器选中画笔工具画笔模式-正常shiftAlt右键 色相轮 上下移动从黑到白亮度变化左右移动从浅到深饱和度的变化选中颜…

Linux 35.5 + JetPack v5.1.3@FUEL编译安装

Linux 35.5 JetPack v5.1.3FUEL编译安装 1. 源由2. 编译&安装Step 1&#xff1a;依赖库安装Step 2&#xff1a;建立工程Step 3&#xff1a;编译工程Step 4&#xff1a;安装工程 3. 问题汇总3.1 fuel_planner/exploration_manager - dw3.2 fuel_planner/plan_env - OpenCV库…

IDEA的使用配置Maven(及selenium+webdriver的下载配置)

一. 下载maven 1. maven官网下载链接 2.​​安装第二行第一列的zip压缩包 ​​​​​​​​ 二. 配置环境变量 1.新建环境变量 2.在系统变量Path环境变量中添加%Maven_HOME%\bin 三.验证环境变量是否配置成功 winr >cmd>mvn -v 如果出现Maven的版本信息&#xff0…

隐藏 IP 地址的重要性是什么?

在当今的数字时代&#xff0c;保护我们的在线身份至关重要。从保护个人信息到保护隐私&#xff0c;互联网用户越来越多地寻求增强在线安全性的方法。保持匿名和保护敏感数据的一个关键方面是隐藏您的 IP 地址。在这篇博文中&#xff0c;我们将深入探讨隐藏 IP 地址的重要性&…

Druid监控页面无法打开(404)

网上教程 我得到的结果 解决 如果localhost:7080/druid/login.html 无法打开Druid监控页面&#xff0c;那么说明Druid数据库连接池根本就没有配置成功&#xff0c;所以才会出现404. 上面配置不成功&#xff0c;要么是配置问题&#xff0c;要么就是版本不兼容问题&#xff08;大…

推荐一个免费的相亲工具

推荐一个免费的相亲工具&#xff0c;步骤如下&#xff1a; 1&#xff09;微信里面搜索公众号“光源桥”&#xff0c;并关注 2&#xff09;输入搜索条件进行搜索对象 例如下面搜索&#xff1a;

Kotlin 网络请求小例子(Ktor)

文章目录 导入依赖创建 Http 客户端 其实还是借着 Ktor 学一学 Kotlin 如何导入依赖&#xff0c;这应该是我们 Kotlin 基础专栏的最后一期了。 Ktor 是 Kotlin 官方的一个网络请求库&#xff0c;它具有优秀且精炼的 API&#xff0c;并且是跨平台的。 本教程参考自 Ktor 文档 …

最强总结!18个机器学习核心算法模型!!

前言 大家好~在学习机器学习之后&#xff0c;你认为最重要的算法模型有哪些&#xff1f;今儿的内容涉及到 线性回归逻辑回归决策树支持向量机朴素贝叶斯K近邻算法聚类算法神经网络集成方法降维算法 我把每种算法模型的核心公式和代码也列举了出来&#xff0c;如果有其他比较重…

自动化测试-Selenium-元素定位

一.元素定位 因为使用selenium进行自动化测试&#xff0c;元素定位是必不可少的&#xff0c;所以这篇文章用于自动化测试中的selenium中的元素定位法。 1.根据id属性进行定位&#xff08;id是唯一的&#xff09; id定位要求比较高&#xff0c;要求这个元素的id必须是固定且唯…

vue的elementUI的el-tree的选择

有一棵树型的数据,需要实现:在外部加一个 全选和不全选的按钮,去全部勾选树结构里面每一项的选框。 当点击勾选全选的时候,树的每一项都勾选; 当取消全选的时候,树的每一项都不勾选; 当选树的其中一项时,全选按钮是半选状态; 实现效果如下: <template><…

JVM(Java虚拟机)、JMM(Java内存模型)笔记

面试常见&#xff1a; 请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?什么是OOM&#xff0c;什么是栈溢出StackOverFlowError? 怎么分析?JVM的常用调优参数有哪些?内存快照如何抓取&#xff1f;怎么分析Dump文件&#xff1f;谈谈JVM中&#xff0c;类加载器你的认识…

上位机图像处理和嵌入式模块部署(f407 mcu开发板基本测试)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 我们拿到一个新的开发板之后&#xff0c;一般都需要测试一下基本功能。这里面一部分功能是我们比较熟悉的&#xff0c;比如说led、key输入、串口、…

计算机网络学习2

文章目录 信道复用技术 第三章数据链路层概述数据链路层的三个重要问题封装成帧和透明传输差错检测可靠传输的相关基本概念可靠传输的实现机制停止等待协议回退N帧协议选择重传协议 点对点协议PPP共享式以太网网络适配器和MAC地址CSMA_CD协议的基本原理共享式以太网的争用期共享…