在上一节,我们学习了限流的方案:
- 限流算法(如:令牌桶、漏桶、计数器)
- 应用层解决
- 接入层解决(如Nginx)
我们知道RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流,并且根据系统的实际情况来调整生成token的速率。本文通过仿写RateLimiter来实现限流,下面直接实现代码(讲解主要的代码):
1.Controller层:
@RequestMapping("/myOrder")
@ExtRateLimiter(value = 10.0, timeOut = 500)
public String myOrder() throws InterruptedException {
System.out.println("myOrder");
return "SUCCESS";
}
2.自定义注解:
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRateLimiter {
double value();
long timeOut();
}
3.AOP切面类:由于每个方法都基本会用到,所以写成切面类,避免代码的重复编写
@Aspect
@Component
public class RateLimiterAop {
// 存放接口是否已经存在
private static ConcurrentHashMap<String, RateLimiter> rateLimiterMap = new ConcurrentHashMap<String, RateLimiter>();
@Pointcut("execution(public * com.xxx.api.*.*(..))")
public void rlAop() {
}
@Around("rlAop()")
public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
// 使用Java反射技术获取方法上是否有@ExtRateLimiter注解类
ExtRateLimiter extRateLimiter = signature.getMethod().getDeclaredAnnotation(ExtRateLimiter.class);
if (extRateLimiter == null) {
// 正常执行方法
Object proceed = proceedingJoinPoint.proceed();
return proceed;
}
// ############获取注解上的参数 配置固定速率 ###############
// 获取配置的速率
double value = extRateLimiter.value();
// 获取等待令牌等待时间
long timeOut = extRateLimiter.timeOut();
RateLimiter rateLimiter = getRateLimiter(value, timeOut);
// 判断令牌桶获取token 是否超时
boolean tryAcquire = rateLimiter.tryAcquire(timeOut, TimeUnit.MILLISECONDS);
if (!tryAcquire) {
serviceDowng();
return null;
}
// 获取到令牌,直接执行..
Object proceed = proceedingJoinPoint.proceed();
return proceed;
}
// 获取RateLimiter对象
private RateLimiter getRateLimiter(double value, long timeOut) {
// 获取当前URL
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String requestURI = request.getRequestURI();
RateLimiter rateLimiter = null;
if (!rateLimiterMap.containsKey(requestURI)) {
// 开启令牌通限流
rateLimiter = RateLimiter.create(value); // 独立线程
rateLimiterMap.put(requestURI, rateLimiter);
} else {
rateLimiter = rateLimiterMap.get(requestURI);
}
return rateLimiter;
}
// 服务降级
private void serviceDowng() throws IOException {
// 执行服务降级处理
System.out.println("执行降级方法,服务器忙!请稍后重试!");
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = attributes.getResponse();
response.setHeader("Content-type", "text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
try {
writer.println("执行降级方法,服务器忙!请稍后重试!");
} catch (Exception e) {
} finally {
writer.close();
}
}
}