1.引入限流算法--令牌桶 和AOP依赖
<!--引入aop依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--限流令牌桶依赖 guava一个由谷歌管理的工具库 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.0-jre</version>
</dependency>
2.自定义限流注解:
//该元注解表示定义的注解只能用于方法上
@Target(value = ElementType.METHOD)
//该元注解表示自定义注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在(用于在运行时动态获取注解信息)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface RateLimiterAnnotation {
//每秒生成令牌的速率
double speed();
//获取令牌的超时时间
long timeout();
}
3.定义AOP切面,使用环绕通知实现限流
/**
* @author oulei
* @服务限流切面
* @Order:控制的切面的执行顺序,越小越选执行,后结束
* @date 2019/12/4 11:27
*/
@Component
@Aspect
@Slf4j
@Order(10)
public class RateLimiterAspect {
/**
* 存放每个接口的令牌桶
*/
private ConcurrentHashMap<String, RateLimiter> limiterMap=new ConcurrentHashMap<String, RateLimiter>();
/**
* 定义切入点(需要切面拦截的包)
* 拦截com.demo.distributed包和所有子包里的任意类的任意方法的执行
*/
@Pointcut("execution(* com.demo.distributed..*.*(..))")
public void pointCut() {
}
/**
* 环绕通知内实现限流
*/
@Around("pointCut()")
public Object around (ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
log.info("进入环绕通知");
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
// 使用Java反射技术获取方法上是否有@RateLimiterAnnotation注解类
RateLimiterAnnotation rateLimiterAnnotation=signature.getMethod().getDeclaredAnnotation(RateLimiterAnnotation.class);
if(rateLimiterAnnotation == null){
Object proceed=proceedingJoinPoint.proceed();
return proceed;
}
//如果在方法上获取到RateLimiter注解就继续执行
//获取注解上的令牌生产速率值
double speed=rateLimiterAnnotation.speed();
//获取注解上获取令牌的超时时间
long timeOut=rateLimiterAnnotation.timeout();
RateLimiter rateLimiter=getRateLimiter(speed);
boolean acquireBool = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
//指定时间内没有获取到令牌,走服务的降级处理
if(!acquireBool){
serviceDegraded();
}
Object proceed = proceedingJoinPoint.proceed();
return proceed;
}
/**
* 获取RateLimiter对象
*/
public RateLimiter getRateLimiter(Double speed){
String requestURI = getRequestUrl();
RateLimiter rateLimiter = limiterMap.get(requestURI);
if(rateLimiter==null){
rateLimiter = RateLimiter.create(speed);
limiterMap.put(requestURI, rateLimiter);
}
return rateLimiter;
}
/**
* 服务降级处理
*/
public void serviceDegraded() throws IOException {
// 执行服务降级处理
log.info("请求url为"+getRequestUrl()+"的方法执行服务的降级处理--");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = attributes.getResponse();
response.reset();
response.setHeader("Content-type", "text/html;charset=UTF-8");
OutputStream outputStream=response.getOutputStream();
try {
outputStream.write("服务器忙!请稍后重试!".getBytes());
} catch (Exception e) {
log.info("降级处理发生异常:"+e.getMessage());
} finally {
outputStream.close();
}
}
/**
* 获取当前请求的url
*/
public String getRequestUrl(){
// 获取当前的请求url
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String requestURI = request.getRequestURI();
return requestURI;
}
}