自定义注解+AOP+RateLimiter实现限流

SpringBoot用自定义注解+AOP+RateLimiter实现限流

 使用Guava的RateLimiter实现限流,每秒最大10个请求,仅适用于单体
 不保证公平访问
 允许先消费,后付款,就是它可以来一个请求的时候一次性取走几个或者是剩下所有的令牌甚至多取
 但是后面的请求就得为上一次请求买单,它需要等待桶中的令牌补齐之后才能继续获取令牌
 所以实际上每秒能够通过的数量会比设置的permitsPerSecond大
 在设置permitsPerSecond的时候应比实际估计的流量要小
 限制能卖的总包子

1.注解

@Inherited
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
	// 每秒发放许可数
	double permitsPerSecond() default 10;
	// 超时时间,即能否在指定内得到令牌,如果不能则立即返回,不进入目标方法/类
	// 默认为0,即不等待,获取不到令牌立即返回
	long timeout() default 0;
	// 超时时间单位,默认取毫秒
	TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

2.切面

//@Scope默认为单例模式
@Scope
@Aspect
@Component
public class RateLimitAspect {

	// com.google.common.collect.Maps 别导错包了
	// 存放RateLimiter,一个url对应一个令牌桶
	private Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();
	
	@Pointcut("@annotation(com.xxxxxx.annotation.RateLimit)")
	private void pointcut() {
	}

	@Around(value = "pointcut()")
	public Object around(ProceedingJoinPoint joinPoint) {
		Object obj = null;
		try {
			// 获取注解
			RateLimit rateLimit = ((MethodSignature) joinPoint.getSignature()).getMethod()
					.getAnnotation(RateLimit.class);
			// 获取request,然后获取请求的url,存入map中
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
					.getRequest();
			String url = request.getRequestURI();
			// 若获取注解不为空
			if (rateLimit != null) {
				// 获取注解的permitsPerSecond与timeout
				double permitsPerSecond = rateLimit.permitsPerSecond();
				long timeout = rateLimit.timeout();
				TimeUnit timeUnit = rateLimit.timeUnit();
				RateLimiter rateLimiter = null;
				// 判断map集合中是否有创建有创建好的令牌桶
				// 若是第一次请求该url,则创建新的令牌桶
				if (!limitMap.containsKey(url)) {
					// 创建令牌桶
					rateLimiter = RateLimiter.create(permitsPerSecond);
					limitMap.put(url, rateLimiter);
					System.out.println("请求======>" + url + "创建令牌桶,容量为:" + permitsPerSecond);
				}
				// 否则从已经保存的map中取
				rateLimiter = limitMap.get(url);
				// 若得到令牌
				if (rateLimiter.tryAcquire(timeout, timeUnit)) {
					// 开始执行目标controller
					obj = joinPoint.proceed();
				} else {
					// 否则直接返回错误信息
					obj = Results.error("请求太过频繁,请稍后重试");
					System.out.println("-----请求太过频繁-----");
				}
			}
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return obj;
	}
}

3.Controller

	@RateLimit(permitsPerSecond = 10 ,timeout = 1000 , timeUnit = TimeUnit.MILLISECONDS)
	@GetMapping("/rateLimiterAnno")
	public String rateLimiteranno(){
		System.out.println("-----访问成功-----");
		return "成功进入rateLimiteranno()";
	}

4.dependency

依赖加上:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>

参考:
https://segmentfault.com/a/1190000016393473

Guava的RateLimiter类相关解释:
https://www.cnblogs.com/exceptioneye/p/4824394.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值