互联网并发与安全系列教程(03) - RateLimiter使用AOP方式实现限流

在上一节,我们学习了限流的方案:

  • 限流算法(如:令牌桶、漏桶、计数器)
  • 应用层解决
  • 接入层解决(如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();
		}

	}
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值