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

一,实现原理

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

  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!";
    }

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

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

相关文章

生态系统服务功能之土壤保持

大家好&#xff0c;今天我们开始新的生态系统服务功能模块&#xff0c;即土壤保持的相关内容介绍与计算。 土壤保持的定义 所谓土壤保持&#xff0c;指生态系统通过其结构与过程保护土壤&#xff0c;降低雨水的侵蚀能力&#xff0c;减少土壤流失&#xff0c;防止泥沙淤积的功…

JVMの内存泄漏内存溢出案例分析

1、内存溢出 内存溢出指的是程序在申请内存时&#xff0c;没有足够的内存可供分配&#xff0c;导致无法满足程序的内存需求&#xff0c;常见的内存溢出情况包括堆内存溢出&#xff08;Heap Overflow&#xff09;和栈溢出&#xff08;Stack Overflow&#xff09;&#xff1a; …

K210视觉识别模块学习笔记4: 训练与使用自己的模型_识别字母

今日开始学习K210视觉识别模块: 模型训练与使用_识别字母 亚博智能的K210视觉识别模块...... 固件库: maixpy_v0.6.2_52_gb1a1c5c5d_minimum_with_ide_support.bin 文章提供测试代码讲解、完整代码贴出、测试效果图、测试工程下载 这里也算是正式开始进入到视觉识别的领域了…

DataGrip 数据库连接客户端

I DataGrip 安装 1.1安装 打开dmg镜像&#xff0c;将“DataGrip.app”拖入应用程序中&#xff1b; 1.2 Act 打开应用程序,点击试用模式启动软件&#xff0c;然后将“jetbrains-agent-latest”拖到任意位置&#xff0c;然后拖入&#xff0c;弹出对话框&#xff0c;点击“Rest…

IDEA调试前端html报错

IDEA调试前端html报错 报错如下&#xff1a; Waiting for connection to localhost:59004. Please ensure that the browser was started successfully with remote debugging port opened. Port cannot be opened if Chrome having the same User Data Directory is already …

.Net 基于.Net8开发的一个Asp.Net Core Webapi小型易用框架

1.项目结构 该项目是基于.net8开发的Asp.Net Core WebApi后端服务,集成了Efcore,Autofac,Jwt,AutoMapper,Serilog,Quartz,MiniExcel等组件。该框架简单易上手&#xff0c;没有额外的学习成本; 该项目采用了多层结构设计&#xff0c;有利于解耦&#xff0c;包含公共层&#xff0…

1.3Java对象和类

Java作为一种面向对象语言。支持以下基本概念&#xff1a; 多态继承封装抽象类对象实例方法重载 本节我们重点研究对象和类的概念。 对象&#xff1a;对象是类的一个实例&#xff08;对象不是找个女朋友&#xff09;&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个…

Stable Diffusion Webui--安装与使用

最近进行的课程汇报&#xff0c;学习了2023年的CVPR文章《DreamBooth: Fine Tuning Text-to-Image Diffusion Models for Subject-Driven Generation》&#xff0c;因此尝试使用了几种方法对这篇文章的工作进行了一定的复现。本文主要介绍Stable Diffusion Web UI(webui)的安装…

基于多源土地覆盖产品整合的高精度中国森林覆盖数据集

本数据集是结合多源土地覆盖产品的一致性和互补性&#xff0c;基于无云合成影像并结合随机森林分类模型和多级投票策略生产的一套全国范围的高精度森林覆盖数据产品。该数据产品可以提升我国森林资源监测、林业经营管理、森林碳汇计量评价等方面工作的准确性&#xff0c;为森林…

SSL发送邮件时如何配置客户端确保安全性?

怎么使用SSL安全协议通过AokSend发送加密的电子邮件&#xff1f; SSL是一种常用的加密通信协议&#xff0c;用于确保数据在客户端和服务器之间的安全传输。AokSend将讨论如何通过配置客户端确保SSL发送邮件的安全性&#xff0c;并介绍如何使用SSL安全协议通过AokSend发送加密的…

【开源】课程管理平台 JAVA+Vue.js+SpringBoot+MySQL

目录 一、项目介绍 课程管理模块 作业题目模块 考试阅卷模块 教师评价模块 部门角色菜单模块 二、项目截图 三、核心代码 一、项目介绍 Vue.jsSpringBoot前后端分离新手入门项目《课程管理平台》&#xff0c;包括课程管理模块、作业题目模块、考试阅卷模块、教师评价模…

docker安装ubtuntu

☆ 问题描述 纯新手在docker desktop 中安装ubuntu ★ 解决方案 1. 拉取镜像 docker pull ubuntu2.查看镜像是否拉取成功 docker images3. 运行容器 docker run -itd --name <容器名称> ubuntu4. 通过 exec 命令进入 ubuntu 容器 docker exec -it <容器名>…

Java:使用Opencv进行大图找小图

本文将介绍使用Java操作Opencv进行行大图找小图测试&#xff01; 1、OpenCV简介 OpenCV是一个开源计算机视觉库&#xff0c;提供了丰富的图像处理和计算机规觉算法。它支持多种编程语言&#xff0c;包括Java本文将介绍如何在Java中使用OpenCV进行图像处理和计算机视觉任务。 …

C++|set、map模拟实现<——红黑树

目录 一、红黑树的迭代器 1.1红黑树迭代器框架 1.2operator*() && operator->() 1.3operator() 1.4operator--() 1.5operator() && operator!() 1.6begin() && end() 二、如何用红黑树搭配map和set(仿函数) 三、红黑树封装map和set(简易版…

【蓝桥杯国赛】双指针

适用于以下的情境&#xff1a; ① 数组 / 字符串中&#xff0c;有多少个满足情况的连续区间。 ② 数组 / 字符串&#xff0c;合并。 【第十三届pythonB组试题&#xff1a;近似gcd】 1. 题目描述 2. 难度&#xff1a;⭐⭐⭐⭐ 3. 思考分析&#xff1a; 具体参考&#xff…

跟风报考PMP,我真的后悔了

真的太香吧&#xff01; 我一开始没打算报考PMP证书的&#xff0c;但是我看身边很多朋友都因为PMP证书得到了升职加薪&#xff0c;这让我实在是一整个羡慕住了&#xff0c;所以我也去报考了PMP。 报考PMP前期我做了什么&#xff1f; 由于我是零基础&#xff0c;没有什么项目…

封装uview-plus上传组件up-upload,支持v-model绑定

痛点 vue上传组件拿到了一般无法直接使用&#xff0c;需要对其上下传的接口按照业务进行处理及定制。本次拿到的uview-plus也是一样&#xff0c;对其上传组件up-upload进行封装&#xff0c;令其更方便开发 目标 封装希望达到的目标&#xff0c;就是实现v-model的绑定。令其支…

SQL 语言:基本概述和数据定义

文章目录 1. 数据库语言2. SQL 概述2.1 SQL 的特点2.2 SQL 语言支持三级模式结构2.3 SQL 的基本组成 3. 数据定义3.1 数据类型3.2 创建表3.3 修改和删除表3.4 创建和删除索引3.5 创建和删除视图 1. 数据库语言 数据结构化语言 (Structured Query Language&#xff0c;SQL)&…

Sui Nami Bags对NFT使用案例进行创新

在四月的Sui Basecamp活动中&#xff0c;与会者体验了一系列Sui技术&#xff0c;这些技术以Nami Bags的形式呈现&#xff0c;这些数字礼包里满是来自Sui生态的NFT和优惠券。通过Enoki&#xff08;Mysten Labs的新客户参与平台&#xff09;提供支持&#xff0c;即使没有加密钱包…

【设计模式深度剖析】【B】【结构型】【对比】| 主要区别包装的不同

&#x1f448;️上一篇:享元模式 回 顾&#xff1a;结构型设计模式 1.代理模式&#x1f448;️ 2.装饰器模式&#x1f448;️ 3.适配器模式&#x1f448;️ 4.组合模式&#x1f448;️ 5.桥接模式&#x1f448;️ 6.外观模式&#x1f448;️ 7.享元模式&#x…