学习网站:http://www.mayikt.com/
一、令牌漏桶算法
1.流程设计
!
2.思路分析
- 令牌桶 使用一个集合装(JUC BlockingQueue来装匀速生成的令牌,offer();poll()从队列中取出并且删除返回)
- 使用定时线程池每隔1s向桶装令牌
- 客户端从桶中去拿去令牌
谷歌 Guava限流 提供Api RateLimiter
3.代码实现
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
3.1 基于AOP+RateLimiter实现接口限流
3.1.1 自定义注解
package com.tomdd.anno;
import java.lang.annotation.*;
/**
* 限流注解
*
* @author tom
*/
@Inherited
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TomDdAccessLimiter {
/**
* 限流名称,如果为空位方法名
*
* @return 限流名称
*/
String name() default "";
/**
* 每秒访问的次数,默认为1
*
* @return qbs
*/
int qbs() default 1;
/**
* 限流提示信息
*
* @return 提示信息
*/
String msg() default "";
}
3.1.2 自定义AOP
package com.tomdd.limitingaop;
import com.google.common.util.concurrent.RateLimiter;
import com.tomdd.anno.TomDdAccessLimiter;
import com.tomdd.resp.BaseResponse;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* 限流AOP拦截
*
* @author zx
* @date 2022年12月19日 9:22
*/
@Component
@Aspect
public class AccessLimiterAop {
private final ConcurrentHashMap<String, RateLimiter> rateLimiterConcurrentHashMap = new ConcurrentHashMap<>();
@Pointcut("@annotation(com.tomdd.anno.TomDdAccessLimiter)")
public void accessLimiter() {
}
/**
* 环绕通知
*/
@Around("accessLimiter()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 1.获取拦截到目标方法
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
// 2.获取方法
Method method = methodSignature.getMethod();
TomDdAccessLimiter accessLimiter = method.getDeclaredAnnotation(TomDdAccessLimiter.class);
// 3.获取注解配置限流的名称
String name = accessLimiter.name();
if(StringUtils.isBlank(name)){
//方法名称作为限流名称
name = method.getName();
}
RateLimiter rateLimiter = rateLimiterConcurrentHashMap.get(name);
if(Objects.isNull(rateLimiter)){
synchronized (this){
if(Objects.isNull(rateLimiter)){
rateLimiter = RateLimiter.create(accessLimiter.qbs());
rateLimiterConcurrentHashMap.put(name,rateLimiter);
}
}
}
boolean result = rateLimiter.tryAcquire();
if(!result){
return BaseResponse.faile(accessLimiter.msg());
}
return pjp.proceed();
}
}
3.1.3 测试
package com.tomdd.controller;
import com.google.common.util.concurrent.RateLimiter;
import com.tomdd.anno.TomDdAccessLimiter;
import com.tomdd.resp.BaseResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <h1>令牌桶限流控制类</h1>
*
* @author zx
* @date 2022年12月19日 9:10
*/
@RestController
@Api(tags = "令牌桶限流控制类:TokenBucketController")
public class TokenBucketController {
private final RateLimiter rateLimiter = RateLimiter.create(2);
@GetMapping("/getTokenBucket")
@ApiOperation("获取令牌桶")
public BaseResponse<?> getTokenBucket(){
boolean result = rateLimiter.tryAcquire();
if(!result){
return BaseResponse.faile("接口限流,没有获取到token");
}
return BaseResponse.ok();
}
@GetMapping("/accessLimiter")
@ApiOperation("基于aop+ratelimiter实现接口限流")
@TomDdAccessLimiter(name="accessLimiter",qbs = 2,msg = "每秒访问2次,请稍后再试!!!!")
public BaseResponse<?> accessLimiter(){
return BaseResponse.ok();
}
}