使用 Spring AOP 和 Guava RateLimiter 实现 API 限流

server/2024/9/24 21:20:33/

在高并发的应用场景下,合理的限流策略是保证系统稳定性的重要手段之一。限流可以防止系统资源被耗尽,避免雪崩效应的发生。本文将介绍如何使用 Spring AOP 和 Guava RateLimiter 实现API限流,并支持自定义限流超时时间。

引入依赖

首先,需要在 pom.xml 中引入 Guava 依赖:

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

定义注解

其次,定义一个 @ApiRateLimit 注解,在需要限流的方法上标注该注解:

java">import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiRateLimit {double qps() default 1; // 每秒钟生成令牌的速率long timeout() default 0; // 尝试获取令牌的超时时间TimeUnit timeUnit() default TimeUnit.SECONDS; // 超时时间单位
}
  • qps 参数控制每秒生成令牌数,即控制限流速率
  • timeouttimeUnit 参数控制获取令牌的超时时间,0表示无超时

实现切面

接下来实现 ApiRateLimitAspect 切面类,在方法执行前通过 RateLimiter 判断是否被限流:

java">import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;@Aspect
@Component
public class ApiRateLimitAspect {private final Map<String, RateLimiter> rateLimiters = new ConcurrentHashMap<>();@Before("@annotation(apiRateLimit)")public void limit(JoinPoint joinPoint, ApiRateLimit apiRateLimit) {String methodName = joinPoint.getSignature().toLongString();double qps = apiRateLimit.qps();RateLimiter limiter = rateLimiters.computeIfAbsent(methodName, k -> RateLimiter.create(qps));long timeout = apiRateLimit.timeout();TimeUnit timeUnit = apiRateLimit.timeUnit();if (timeout > 0) {if (!limiter.tryAcquire(timeout, timeUnit)) {throw new RuntimeException("API rate limit exceeded");}} else {if (!limiter.tryAcquire()) {throw new RuntimeException("API rate limit exceeded");}}}
}
  • 使用 ConcurrentHashMap 缓存每个方法对应的 RateLimiter 实例
  • 根据 @ApiRateLimit 注解的参数尝试获取令牌
    • 若超时时间大于0,使用 tryAcquire(timeout, timeUnit) 获取令牌
    • 若超时时间为0,使用 tryAcquire() 获取令牌
  • 如果无法获取令牌,抛出 RuntimeException 限流异常

使用示例

在 Controller 方法上标注 @ApiRateLimit 注解即可实现限流:

java">@RestController
public class DemoController {@GetMapping("/test")@ApiRateLimit(qps = 2, timeout = 200, timeUnit = TimeUnit.MILLISECONDS)public String test() {return "hello world";}
}

上述代码对 /test 接口限流,限流速率为每秒 2 个请求,获取令牌超时时间为 200 毫秒。

总结

通过简单的注解和 AOP 切面,就可以实现 API 限流功能,并支持自定义限流速率和限流超时时间。这种实现方式无侵入性,添加或移除限流只需要在方法上增加或移除注解即可,降低了维护成本。

值得注意的是,在分布式环境下,单机限流的方式可能无法满足需求,我们需要结合分布式限流组件如 Redis 等来实现全局限流。另外,限流策略也可以根据实际场景进行优化,如配合熔断、降级等策略使用,以提高系统的稳定性。


http://www.ppmy.cn/server/5308.html

相关文章

【数据库】MySQL之union联合查询

联合查询&#xff1a; 关键字union,作用就是将多条查询语句的结果合并成一个结果集。 案例&#xff1a;&#xff08;查询部门编号大于100或名字中含有a字符的员工信息&#xff09; select * from employees where department_id>100 or last_name like %a%; 用联合查询演…

Uipath用计划任务启动 bat脚本语句

方法一 cd C:\Program Files\UiPath\Studio UiRobot execute --file "Uipath路径\project.json" 这种是用UiRobot.exe来启动任务的 方法二 "C:\Progra~1\UiPath\Studio\UiRobot.exe" -file "C:\Users\Documents\UiPath\se_Windows.1.0.3.nupkg&q…

Rust---特征(Trait)

特征定义了一组可以被共享的行为,只要实现了特征,你就能使用这组行为。 目录 如何定义特征特征中定义具有默认实现的方法如何实现特征使用特征作为函数参数特征约束(trait bounds)单个特征约束多个特征约束接受两个参数where约束如何定义特征 特征只定义行为看起来是什么样…

代码随想录算法训练营第四十五天|70.爬楼梯、322.零钱兑换、279.完全平方数

代码随想录算法训练营第四十五天|70.爬楼梯、322.零钱兑换、279.完全平方数 70.爬楼梯 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 注意&#xff1a;给定 n 是一…

Java compare compareTo方法区别详解

compareTo compareTo(Object o)方法是java.lang.Comparable<T>接口中的方法&#xff0c;当需要对某个类的对象进行排序时&#xff0c;该类需要实现Comparable<T>接口的&#xff0c;必须重写public int compareTo(T o)方法。 它强行将实现它的每一个类的对象进行整…

N-149基于微信小程序网上商城系统

开发工具&#xff1a;IDEA、微信小程序 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 前端技术&#xff1a;vue、uniapp 服务端技术&#xff1a;springbootmybatisredis 本系统分微信小程序和管理后台两部分&a…

密码学 | 椭圆曲线密码学 ECC 入门(四)

目录 正文 1 曲线方程 2 点的运算 3 求解过程 4 补充&#xff1a;有限域 ⚠️ 知乎&#xff1a;【密码专栏】动手计算双线性对&#xff08;中&#xff09; - 知乎 ⚠️ 写在前面&#xff1a;本文属搬运博客&#xff0c;自己留着学习。注意&#xff0c;这篇博客与前三…

● State Schema Evolution的平滑迁移策略

State Schema Evolution指的是在分布式系统或数据库中&#xff0c;随着业务需求的发展和变化&#xff0c;需要对存储的状态&#xff08;如数据库表结构、数据模型等&#xff09;进行升级或调整的过程。平滑迁移策略的目标是在不影响系统正常运行、尽量减少服务中断时间的前提下…