之前有个RateLimiter代码实现限流的文章,这里附带一下,感兴趣的童鞋可以看一下下面的这个链接【Guava】使用RateLimiter限制用户操作频率_阿小冰的博客-CSDN博客【Guava】使用RateLimiter限制用户操作频率https://blog.csdn.net/qq_38377525/article/details/125053866
下面我们提供一个注解版的,个人推荐使用注解版,因为上面链接提到的这一版代码太冗余了,所以不太推荐
代码演示
1、创建一个SpringBoot工程
2、添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.0.1-jre</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.6</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.11</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> <version>2.3.6.RELEASE</version> </dependency>
2、编写注解类
@Target(value = ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MyRateLimiter { //向令牌桶放入令牌的速率 double rate(); //从令牌桶获取令牌的超时时间 long timeout() default 0; }
3、编写切面实现类
@Aspect @Component @Configuration public class MyRateLimiterAspect { private RateLimiter rateLimiter = RateLimiter.create(2); @Pointcut("@annotation(MyRateLimiter注解类全路径com.xxx)") public void pointcut() { } /*** 核心切面方法 */ @Around("pointcut()") public Object process(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature(); //使用反射获取方法上是否存在@MyRateLimiter注解 MyRateLimiter myRateLimiter = signature.getMethod().getDeclaredAnnotation(MyRateLimiter.class); if (myRateLimiter == null) { //程序正常执行,执行目标方法 return proceedingJoinPoint.proceed(); } //获取注解上的参数 // 获取配置的速率 double rate = myRateLimiter.rate(); //获取客户端等待令牌的时间 long timeout = myRateLimiter.timeout(); //设置限流速率 rateLimiter.setRate(rate); //判断客户端获取令牌是否超时 boolean tryAcquire = rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS); if (!tryAcquire) { //服务降级 fullback(); return null; } //获取到令牌,直接执行 return proceedingJoinPoint.proceed(); } /*** 降级处理 */ private void fullback() { ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletResponse response=attributes.getResponse(); response.setHeader("Content-type", "text/html;charset=UTF-8"); PrintWriter writer = null; try { writer = response.getWriter(); writer.println("当前访问人数过多,请稍后再试!"); writer.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (writer != null) { writer.close(); } } } }
4、编写接口
@RestController public class TestController { @MyRateLimiter(rate = 1.0, timeout = 500) @RequestMapping("/pay") public String pay() { //记录返回接口 return "支付成功!"; } }
5、验证接口是否限流
频繁访问接口,会发现返回结果会在"支付成功"或"当前访问人数过多,请稍后再试!"来回切换,这就达到了限流的效果