使用Google工具类Guava自定义一个@Limiter接口限流注解

news2024/11/26 22:22:30

在Springboot中引用RateLimiter工具类依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>30.1-jre</version>
</dependency>

需要注意的是,Guava 的不同版本可能会有一些差异,因此建议根据自己的实际情况选择合适的版本。另外,如果你使用 Gradle 或其他构建工具来管理项目依赖,也可以根据上述 Maven 依赖进行相应的配置。

自定义一个@Limiter注解

import java.lang.annotation.*;

/**
 * @author admin
 * @Description 接口限流
 *
 * 注解 @Inherited 和 @Documented 的区别:
 * -> @Inherited允许其他注解继承该注解。
 * -> @Documented 可以被例如 javadoc此类的工具文档化,Documented是一个标注注解,没有成员。
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Limiter {

    // 接口名称
    String name() default "";

    // 接口限流速率,默认20
    int value() default 20;

}

定义切面类 LimiterAspect 实现@Limiter注解

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.RateLimiter;
import com.hrbb.common.core.utils.StringUtils;
import com.hrbb.risk.config.ConstantConfig;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @Author admin
 * @Description 接口限流注解
 **/
@Aspect
@Component
public class LimiterAspect {

    private static final String STR_SPLIT_ = "_";
    private static final Logger log = LoggerFactory.getLogger(LimiterAspect.class);

    /**
     * 通过get()方法获取限流实例
     * 如果实例不存在,则默认创建一个限流实例
     * 参数可以根据需求灵活定义
     */
    private static LoadingCache<String, RateLimiter> cacheMap = CacheBuilder.newBuilder()
            .expireAfterWrite(ConstantConfig.NUMBER_10, TimeUnit.SECONDS)
            .build(new CacheLoader<String, RateLimiter>() {
                @Override
                public RateLimiter load(String key) throws Exception {
                    String[] split = key.split(STR_SPLIT_);
                    log.info("系统限流:[{}]方法创建限流实例, 实例过期时间[10]秒,限流速率每秒[{}]QPS/s", split[0], split[1]);
                    cacheMap.put(key, RateLimiter.create(Double.valueOf(split[1])));
                    return cacheMap.get(key);
                }
            });


    @Before(value = "@annotation(limiter)")
    public void doBefore(JoinPoint joinPoint, Limiter limiter){

        // 如果没有设置接口限流名称、则默认取方法名称为key
        String limitName = limiter.name();
        if (StringUtils.isBlank(limiter.name())){
            limitName = joinPoint.getSignature().getName();
        }

        // 使用接口名称和过期时间,拼接keyName,然后在load中获取相关的参数
        String keyName = limitName + STR_SPLIT_ + limiter.value();
        try {
            // 创建限流实例
            RateLimiter rateLimiter = cacheMap.get(keyName);
            if (!rateLimiter.tryAcquire()) {
                log.error("接口请求失败,当前接口名称{},每秒请求速率超过{} QPS/s", limitName, limiter.value());
                //throw new RuntimeException("Rate limit exceeded");
            }
        }catch (Exception e){
            throw new RuntimeException(e.getMessage(),e);
        }

    }

}

验证注解

创建 TestLimiterController 类,查看注解在接口中的使用情况

import com.hrbb.common.core.web.domain.AjaxResult;
import com.hrbb.risk.annotation.Limiter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

/**
 * @Author admin
 * @Description 接口限流测试
 **/
@RestController
@RequestMapping("/test")
public class TestLimiterController {

    private static final Logger logger = LoggerFactory.getLogger(TestLimiterController.class);

    @Limiter(name = "limiterDemo", value = 30)
    @GetMapping("/limiterDemo")
    public AjaxResult limiterDemo()
    {
        logger.info("接口响应成功!");
        return AjaxResult.success("响应成功");
    }
}

使用 jmeter 压测,50个线程、30秒

控制台打印信息

 

关于 CacheBuilder.newBuilder().expireAfterWrite 

CacheBuilder.newBuilder().expireAfterWrite 方法本身是线程安全的,可以在多线程环境下使用。它返回的是一个 CacheBuilder 对象,该对象是不可变的,因此可以被多个线程共享。

然而,如果你使用 CacheBuilder 创建了一个缓存对象,并且在多个线程中同时访问该缓存对象,那么就需要注意线程安全问题了。具体来说,如果多个线程同时对同一个缓存对象进行读写操作,可能会导致数据不一致或者并发异常等问题。

为了避免这种情况,可以考虑使用 CacheLoader 或者 LoadingCache 来创建缓存对象,这样可以确保缓存对象的加载和更新是线程安全的。例如:

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .build(new CacheLoader<String, String>() {
            @Override
            public String load(String key) throws Exception {
                // 从数据库或其他数据源中加载数据
                return loadDataFromDatabase(key);
            }
        });

在上面的示例中,我们使用 CacheLoader 来创建缓存对象,并在其中实现了数据加载的逻辑。由于 CacheLoader 是线程安全的,因此可以确保缓存对象的加载和更新是线程安全的。

@Aspect 注解

@Aspect 是 Spring AOP 中的一个注解,用于声明一个切面类。切面类是一个普通的 Java 类,其中包含了一些切点和通知等组件,用于对目标对象进行增强。

具体来说,@Aspect 注解可以用在类上,表示该类是一个切面类。在切面类中,可以使用其他注解来定义切点和通知等组件,例如:

  • @Pointcut:定义一个切点,用于匹配目标对象中的方法。
  • @Before:定义一个前置通知,在目标方法执行之前执行。
  • @After:定义一个后置通知,在目标方法执行之后执行。
  • @Around:定义一个环绕通知,在目标方法执行前后都可以执行自定义逻辑。
  • @AfterReturning:定义一个返回通知,在目标方法返回结果之后执行。
  • @AfterThrowing:定义一个异常通知,在目标方法抛出异常时执行。

此外,还有一些其他的注解可以用于定义切面组件,具体可以参考 Spring AOP 的文档

Spring AOP 官方文档

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

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

相关文章

新手第一次做性能测试?性能测试流程详全,从需求到报告一篇打通

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、确认需求 确定…

3、互联网行业及产品经理分类

上一篇文章&#xff1a;2、产品经理的工作内容_阿杰学编程的博客-CSDN博客 1、产品经理分类 我们把产品经理划分成这样两个大的类型&#xff0c;一个是传统行业的&#xff0c;一个是互联网行业的。这个简单了解一下就行。 这个里面会发现绝大多数也是体育劳动&#xff0c;你比…

软件测试岗位都是女孩子在做吗?

听我一朋友说&#xff0c;测试岗位基本都是女孩子做。” 不知道是不是以前“软件测试岗”给人印象是“不需要太多技术含量”的错觉&#xff0c;从而大部分外行认为从业软件测试的人员中女生应占了大多数。比如有人就觉得&#xff1a;软件测试主要是细心活&#xff0c;所以女生…

2023 年各大互联网公司常见面试题(Java 岗)汇总

很多人都说今年对于 IT 行业根本没有所谓的“金三银四”“金九银十”。在各大招聘网站或者软件上不管是大厂还是中小公司大多都是挂个招聘需求&#xff0c;实际并不招人&#xff1b;在行业内的程序员基本都已经感受到了任老前段时间口中所谓的“寒气”。 虽然事实确实是如此&a…

30个接口自动化测试面试题,赶紧收藏

1. 什么是接口自动化测试&#xff1f; 答&#xff1a;接口自动化测试是指使用自动化工具对接口进行测试&#xff0c;验证接口的正确性、稳定性和性能等方面的指标。2. 为什么要进行接口自动化测试&#xff1f; 答&#xff1a;接口自动化测试可以提高测试效率&#xff0c;减少人…

新能源行业如何进行数据防泄漏

客户情况 某新能源电池企业专业从事于新能源锂离子动力电池和储能电池的研发、生产和销售&#xff0c;具备电芯、模组、BMS及Pack的完整资源开发能力。公司致力于通过持续不断地改进电池技术&#xff0c;为全球锂离子动力和储能领域提供数字化精准高效的新能源解决方案。 该企…

Nautilus Chain 主网上线在即,一文盘点该生态即将上线的项目

Nautilus Chain 是行业内第一个并行化&#xff0c;且运行速度最快 EVM Rollup 的L3扩容方案&#xff0c;作为首个模块化链&#xff0c;存储、计算、共识等都在不同的模块中&#xff0c;意味着其能够获得更高的可拓展性与扩容能力&#xff0c;并在Layer2的基础上进一步提升了网络…

SpringCloud Alibaba-Sentinel

SpringCloud Alibaba-Sentinel 1. Sentinel核心库1.1 Sentinel介绍1.2 Sentinel核心功能1.2.1 流量控制1.2.2 熔断降级 2 Sentinel 限流熔断降级2.1 SentinelResource定义资源2.1.1 blockHandler/blockHandlerClass2.1.2 fallback/fallbackClass2.1.3 defaultFallback 2.2 Sent…

2、产品经理的工作内容

上一篇文章&#xff1a;1、产品经理的宏观定义_阿杰学编程的博客-CSDN博客 接下来这个章节里&#xff0c;我们有三个目标。 第一个通过案例&#xff0c;大家要了解一下产品经理的一个主要的工作内容。 第二个理解产品经理的一个重要性。 第三个我们要熟悉一下MVP的概念&…

Vue实战笔记(四) 引入Mavon Editor

大家好&#xff0c;我是半虹&#xff0c;这篇文章来讲如何在 Vue 中引入 Mavon Editor \text{Mavon Editor} Mavon Editor 1、背景介绍 在上篇文章中&#xff0c;我们介绍过如何在 Vue 中引入富文本编辑器 Quill Editor \text{Quill Editor} Quill Editor 在这篇文章中&…

433/315接收芯片 XL520,SOP8封装,适用于低功耗要求产品

XL520是一款高集成度、 低功耗的单片ASK/0OK射频接收芯片。高频信号接收功能全部集成于片内以达到用最少的外围器件和最低的成本获得最可靠的接收效果。 XL520接收芯片为SOP8封装&#xff0c;正常工作电压范围2.0~5.5V&#xff0c;正常工作电流3.0~3.2mA&#xff0c;启动时间2…

pdf可以转换为word文档吗?分享这两个方法给大家!

PDF 是一种常见的文件格式&#xff0c;用于可靠地显示和共享文档。然而&#xff0c;当需要编辑或重用 PDF 内容时&#xff0c;将其转换为可编辑的 Word 文档是一个常见的需求。在本文中&#xff0c;我们将介绍两种方法&#xff0c;以帮助您将 PDF 转换为 Word 文档&#xff0c;…

SpringBoot+Bootstrap图书馆管理系统

主要功能 管理员权限登录&#xff1a; ①管理员拥有最高权限&#xff0c;可以分配角色&#xff0c;使不同角色&#xff08;教师、学生等&#xff09;登录显示不同界面的效果 ②首页、系统设置&#xff1a;菜单管理、角色管理、用户管理、日志管理、数据备份、违规统计、占座统…

Unity基础5——物理检测

一、层级 Layer ​ Unity 中设置了共 32 层 Layer&#xff0c;如图&#xff0c;可以点击 Add Layer 添加自定义的 Layer ​ 通过名字得到层级编号 LayerMask.NameToLayer(string layer) ​ 我们需要通过编号左移构建二进制数&#xff0c;这样每一个编号的层级都是对应位为 1 的…

如何使用Jmeter进行http接口测试?

目录 前言&#xff1a; 一、开发接口测试案例的整体方案&#xff1a; 二、接口自动化适用场景&#xff1a; 三、接口测试环境准备 四、创建工程&#xff1a; 总结&#xff1a; 前言&#xff1a; 本文主要针对http接口进行测试&#xff0c;使用Jmeter工具实现。 Jmter工具设…

HTMLCSS Day08 CSS transition过渡

文章目录 CSS过渡-Transitions-过渡三要素-过渡触发-transition-property 规定应用过渡的 CSS 属性的名称。-transition-duration 定义过渡效果花费的时间。默认是 0。-transition-timing-function 规定过渡效果的时间曲线。默认是 "ease"。-transition-delay 规定过…

史上最全文件类型读写库大盘点!什么?还包括音频、视频?

介绍史上最全PYTHON文件类型读写库大盘点&#xff01;包含常用和不常用的大量文件格式&#xff01;文本、音频、视频应有尽有&#xff01;废话不多说&#xff01;走起来&#xff01; 先给大家快捷总结&#xff1a; 文件格式Python库文本文件内置open函数CSV文件csvJSON文件jso…

信号量实现线程同步代码

信号量&实现线程同步代码 信号量线程同步示例代码 信号量 信号量&#xff08;Semaphore&#xff09;是一种用于多线程编程中的同步工具&#xff0c;用于管理对共享资源的访问。它可以控制同时访问某个资源的线程数量&#xff0c;并提供了对共享资源的互斥访问。 信号量通…

一个支持WinForms换肤的开源组件

博主介绍&#xff1a; &#x1f308;一个10年开发经验.Net老程序员&#xff0c;微软MVP、博客专家、CSDN/阿里云 .Net领域优质创作者&#xff0c;专注于.Net领域知识、开源项目分享&#xff01;&#x1f308; &#x1f6d5;文末获取&#xff0c;加入交流群&#x1f6d5; &#…

java数组(Array)

文章目录 一维数组的使用数组的长度数组元素的引用一维数组的遍历一维数组内存分析 数组元素的默认值多维数组的使用静态初始化动态初始化数组的长度和角标二维数组的遍历内存解析 Arrays工具类的使用 一维数组的使用 int[] arr; int arr1[]; double[] arr2; String[] arr3; …