springboot+RateLimiter+AOP自定义注解限流

news2024/12/23 14:24:50

springboot+RateLimiter+AOP自定义注解限流

  • RateLimiter简介
  • springboot集成RateLimiter
    • pom.xml引入
    • RateLimiter常用api
    • 代码实现
      • 自定义注解Limiter
      • 限流切面
      • 验证

RateLimiter简介

RateLimiter是Guava库中的一个限流器,它提供如下功能:
(1)基于PPS进行限流
(2)基于PPS限流的同时提供热启动

springboot集成RateLimiter

pom.xml引入

<!--引入guava依赖-->
<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>31.1-jre</version>
</dependency>

RateLimiter常用api

(1)RateLimiter.create(double permitsPerSecond); ===> 指定速率,每秒产生permitsPerSecond个令牌
(2)rateLimiter.acquire(int permits); ===> 返回获取permits个令牌所需的时间
(3)rateLimiter.tryAcquire(1); ===> 获取1个令牌,返回false获取失败,true获取成功
(4)rateLimiter.tryAcquire(1, 2, TimeUnit.SECONDS); ===> 获取1个令牌,最多等待两秒。返回false获取失败,true获取成功

package com.mry.springboottools.controller;

import com.google.common.util.concurrent.RateLimiter;

import java.util.concurrent.TimeUnit;

/**
 * RateLimiter测试类
 */
public class RateLimiterTest {

    public static void main(String[] args) {
        //指定速率,每秒产生一个令牌  
        RateLimiter rateLimiter = RateLimiter.create(1);
        System.out.println(rateLimiter.getRate());
        //修改为每秒产生2个令牌
        rateLimiter.setRate(2);
        System.out.println(rateLimiter.getRate());

        //while (true) {  
        //获取2个令牌所需要的时间  
        double acquire = rateLimiter.acquire(2);
        //输出结果, 第一次0秒,后面每次等待接近0.5秒的时间  
        //0.0  
        //0.995198  
        //0.9897  
        //0.999413  
        //0.998272  
        //0.99202  
        //0.993757  
        //0.996198  
        //0.99523  
        //0.99532  
        //0.992674  
        System.out.println(acquire);
        // }  
        //获取1个令牌  
        boolean result = rateLimiter.tryAcquire(1);
        System.out.println(result);
        //获取1个令牌,最多等待两秒  
        boolean result2 = rateLimiter.tryAcquire(1, 2, TimeUnit.SECONDS);
        System.out.println(result2);

    }
}


代码实现

自定义注解Limiter

package com.mry.springboottools.annotation;

import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

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

    /**
     * 不进行限流
     */
    int NOT_LIMITED = 0;

    /**
     * qps (每秒并发量)
     */
    double qps() default NOT_LIMITED;

    /**
     * 超时时长,默认不等待
     */
    int timeout() default 0;

    /**
     * 超时时间单位,默认毫秒
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;

    /**
     * 返回错误信息
     */
    String msg() default "系统忙,请稍后再试";

}


限流切面

package com.mry.springboottools.aspect;

import com.google.common.util.concurrent.RateLimiter;
import com.mry.springboottools.annotation.Limiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 限流切面
 * @author
 * @date
 */
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {
    /**
     * key: 类全路径+方法名
     */
    private static final ConcurrentMap<String, RateLimiter> RATE_LIMITER_CACHE = new ConcurrentHashMap<>();


    @Around("@within(limiter) || @annotation(limiter)")
    public Object pointcut(ProceedingJoinPoint point, Limiter limiter) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        if (limiter != null && limiter.qps() > Limiter.NOT_LIMITED) {
            double qps = limiter.qps();
            //这个key可以根据具体需求配置,例如根据ip限制,或用户
            String key = method.getDeclaringClass().getName() + method.getName();
            if (RATE_LIMITER_CACHE.get(key) == null) {
                // 初始化 QPS
                RATE_LIMITER_CACHE.put(key, RateLimiter.create(qps));
            }
            // 尝试获取令牌
            if (RATE_LIMITER_CACHE.get(key) != null && !RATE_LIMITER_CACHE.get(key).tryAcquire(limiter.timeout(), limiter.timeUnit())) {
                log.error("触发限流操作{}", key);
                throw new RuntimeException(limiter.msg());
            }
        }
        return point.proceed();
    }

}

验证

package com.mry.springboottools.controller;

import com.google.common.util.concurrent.RateLimiter;
import com.mry.springboottools.annotation.Limiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;

/**
 * 测试限流
 * @author
 * @date
 */
@RestController
@RequestMapping("limiter")
@Slf4j
public class RateLimiterController {

    @GetMapping
    @Limiter(qps = 1, msg = "您已被限流!")
    public String getUserName() {
        String userName = "mry";
        log.info("userName = {}", userName);
        return userName;
    }
}

接口请求:
在这里插入图片描述

日志输出:
在这里插入图片描述

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

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

相关文章

论文推荐:基于联合损失函数的多任务肿瘤分割

以FFANet为主干&#xff0c;加入分类的分支&#xff0c;将模型扩展为多任务图像分割框架&#xff0c;设计了用于分类和分割的联合损失函数。 FFANetMTL 1、FFANet和分割分支 FFANet作为骨干网络&#xff0c;作为对VoVNet的重新设计和优化&#xff0c;FFANet在骨干网中加入了残…

SpringMVC请求与响应

文章目录 请求与响应请求映射路径请求传参传递普通参数传递实体类参数传递数组参数传递集合参数传递JSON参数传递日期参数 响应数据 请求与响应 请求映射路径 请求映射路径是通过注解: RequestMapping 类型&#xff1a;方法注解, 类注解 位置&#xff1a;SpringMVC控制器方法…

少儿编程 中国电子学会图形化编程等级考试Scratch编程一级真题解析(判断题)2023年3月

2023年3月scratch编程等级考试一级真题 判断题(共10题,每题2分,共20分) 26、在角色列表区和造型标签页都可以修改角色的名称。 答案:错 考点分析:考查Scratch平台使用,只有角色区可以修改角色的造型名称,造型标签不能修改,所以答案错误 27、可以在角色中切换舞台的…

Java核心技术 卷1-总结-7

Java核心技术 卷1-总结-7 lambda 表达式方法引用构造器引用变量作用域异常分类声明受查异常 lambda 表达式 方法引用 有时&#xff0c; 可能已经有现成的方法可以完成你想要传递到其他代码的某个动作。例如&#xff0c; 假设你希望只要出现一个定时器事件就打印这个事件对象。…

使用@Scope注解设置组件的作用域

前言 Spring容器中的组件默认是单例的&#xff0c;在Spring启动时就会实例化并初始化这些对象&#xff0c;并将其放到Spring容器中&#xff0c;之后&#xff0c;每次获取对象时&#xff0c;直接从Spring容器中获取&#xff0c;而不再创建对象。 1.Scope注解概述 Scope注解能…

【uni-app】【01】底部导航栏与页面切换

1.(配置文件在哪)uni-app 路由控制是在 pages.json文件中的。 2.(基本配置项有哪些)初学的时候主要有三个配置项&#xff0c;①pages ② globalStyle ③ tabbar [!TOC] 接下来主要是对这三个配置项做一个简单介绍。 pages 负责页面管理。不需要自己写的&#xff0c;你在项目的p…

【Scala入门】scala基础语法:类和对象,变量和常量

上一篇请移步【Scala入门】Scala下载及安装&#xff08;Windows&#xff09;以及Idea创建第一个scala项目_水w的博客-CSDN博客 目录 一、Scala 二、Scala基础语法 2.1 注释与标识符规范 2.2 变量与常量 【案例&#xff1a;变量声明和赋值】 2.3 object 【案例&#xff1…

合并二叉树-递归法

1题目 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两个节点重叠…

gdb调试常用指令及案例讲解

文章目录 前言一、常用指令二、案例说明1、测试源文件2、编译和调试 三、其他指令四、案例说明 前言 GDB是一个由GNU开源组织发布的、UNIX/LINUX 操作系统下的、基于命令行的、功能强大的程序调试工具。 GDB 支持断点、单步执行、打印变量、观察变量、查看寄存器、查看堆栈等调…

【JavaEE】_2.文件与IO

目录 1.文件概述 1.1 文件的概念 1.2 文件的存储 1.3 文件的分类 1.4 目录结构 1.5 文件操作 1.5.1 文件系统操作 1.5.2 文件内容操作 2. Java文件系统操作 2.1 File类所处的包 2.2 构造方法 2.3 方法 2.3.1 与文件路径、文件名有关的方法 2.3.2 文件是否存在与普…

Java核心技术 卷1-总结-13

Java核心技术 卷1-总结-13 具体的集合散列集树集队列与双端队列优先级队列 映射基本映射操作 具体的集合 散列集 链表和数组可以有序的存储元素。但是&#xff0c;如果想要查看某个指定的元素&#xff0c;却又忘记了它的位置&#xff0c;就需要访问所有元素&#xff0c;直到找…

vue2数据响应式原理(5) 通过重写函数实现数组响应式监听

其实 我们之前对数组的一个监听 还并不是很完美 我们打开案例 打开 output.js 更改代码如下 import { observe } from "./dataResp" const output () > {var obj {data: {data: {map: {dom: {isgin: true}},arg: 13},name: "小猫猫"},bool: [1,2,3,4…

【经验与Bug】tensorflow草记

文章目录 1 常用小知识2 Learn1) 疑惑未解2) 为何要有"bias"&#xff1f; 3 问题处理1) jupyter的环境指定目录运行jupyter 2) Keras版本3) 为什么accuracy为100%&#xff0c;迭代时参数还在更新&#xff1f; 1 常用小知识 conda activate tf 在anaconda prompt使用&…

Android studio 播放音频文件 播放语速

一、使用 public class MainActivity extends AppCompatActivity {private Hsvolume mHsVolume null;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mHsVolume new Hsvolume(th…

【YOLO系列】YOLOv1论文笔记

论文链接&#xff1a;[1506.02640] You Only Look Once: Unified, Real-Time Object Detection (arxiv.org) YOLO将目标检测看作回归问题&#xff0c;使用单个神经网络直接从完整图像上预测边界框和类别概率。&#xff08;端到端&#xff1a;输入原始数据&#xff0c;输出的是最…

E5EAA HENF105240R1将用于工业生产过程的测量、控制和管理

​E5EAA HENF105240R1将用于工业生产过程的测量、控制和管理 工业控制计算机是工业自动化控制系统的核心设备 工业控制计算机是工业自动化设备和信息产业基础设备的核心。传统意义上&#xff0c;将用于工业生产过程的测量、控制和管理的计算机统称为工业控制计算机&#xff0c;…

SpringBoot整合WebSocket的两种方式及微服务网关Gateway配置

一、说明 项目中后台微服务需要向前端页面推送消息&#xff0c;因此不可避免的需要用到WebSocket技术。SpringBoot已经为WebSocket的集成提供了很多支持&#xff0c;只是WebSocket消息如何通过微服务网关Spring Cloud Gateway向外暴露接口&#xff0c;实际开发过程中遇到了很多…

【数据结构第四章】- 串的模式匹配算法(BF 算法和 KMP 算法/用 C 语言实现)

目录 一、前言 二、BF 算法 三、KMP 算法 3.2.1 - KMP 算法的原理 3.2.2 - KMP 算法的实现 3.2.3 - KMP 算法的优化 创作不易&#xff0c;可以点点赞&#xff0c;如果能关注一下博主就更好了~ 一、前言 子串的定位运算通常称为串的模式匹配或串匹配。此运算的应用非常广…

美国主机的带宽和网络速度究竟有多快?

在选择一个主机时&#xff0c;其带宽和网络速度是非常重要的考虑因素。而美国主机在带宽和网络速度方面有着明显的优势&#xff0c;成为了众多用户的首选。那么&#xff0c;美国主机的带宽和网络速度究竟有多快呢?本文将通过分析美国主机的网络基础设施和数据中心设施&#xf…

golang入门项目——打卡抽奖系统

功能介绍 用户加入群组之后&#xff0c;会在签到群组所设的签到地点进行签到和签退&#xff0c;并限制同一个设备只能签到一个用户&#xff0c;签到成功之后。会获取一定的限制在该群组使用的积分。该群组可以设置一些抽奖活动&#xff0c;用户可使用该群组内的积分来进行该群…