AOP+RateLimiter平滑限流
RateLimiter平滑限流分:平滑突发限流和平滑预热限流
RateLimiter简介(guava的令牌桶实现)
Google开源工具包Guava提供了限流工具类RateLimiter,该类基于令牌桶算法(Token Bucket)来完成限流,非常易于使用.RateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率.它支持两种获取permits接口,一种是如果拿不到立刻返回false,一种会阻塞等待一段时间看能不能拿到.
RateLimiter和Java中的信号量(java.util.concurrent.Semaphore)类似,Semaphore通常用于限制并发量。
源码注释中的一个例子,比如我们有很多任务需要执行,但是我们不希望每秒超过两个任务执行,那么我们就可以使用RateLimiter:
final RateLimiter rateLimiter = RateLimiter.create(2.0); void submitTasks(List<Runnable> tasks, Executor executor) { for (Runnable task : tasks) { rateLimiter.acquire(); // may wait executor.execute(task); } }
另外一个例子,假如我们会产生一个数据流,然后我们想以每秒5kb的速度发送出去.我们可以每获取一个令牌(permit)就发送一个byte的数据,这样我们就可以通过一个每秒5000个令牌的RateLimiter来实现:
final RateLimiter rateLimiter = RateLimiter.create(5000.0); void submitPacket(byte[] packet) { rateLimiter.acquire(packet.length); networkService.send(packet); }
另外,我们也可以使用非阻塞的形式达到降级运行的目的,即使用非阻塞的tryAcquire()方法:
if(limiter.tryAcquire()) { //未请求到limiter则立即返回false doSomething(); }else{ doSomethingElse(); }
快速失败方式。
示例:
import java.lang.reflect.Method; import java.util.concurrent.ConcurrentHashMap; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.util.concurrent.RateLimiter; import ratelimit.annotation.RateLimit; import ratelimit.enums.RateLimitCode; /*@Component @Scope @Aspect*/ public class RateLimitAspect { private Logger logger = LoggerFactory.getLogger(RateLimitAspect.class); //用来存放不同接口的RateLimiter(key为接口名称,value为RateLimiter) private ConcurrentHashMap<String, RateLimiter> map = new ConcurrentHashMap<>(); private RateLimiter rateLimiter; @Pointcut("@annotation(ratelimit.annotation.RateLimit)") public void rateLimit() { } @Around("rateLimit()") public Object around(ProceedingJoinPoint joinPoint) throws NoSuchMethodException { Object obj = null; //获取拦截的方法名 MethodSignature msig = (MethodSignature)joinPoint.getSignature(); Method currentMethod = msig.getMethod(); //获取注解信息 RateLimit annotation = currentMethod.getAnnotation(RateLimit.class); int limitNum = annotation.limitNum(); //获取注解每秒加入桶中的token String functionName = currentMethod.getDeclaringClass().getName() + "#" + currentMethod.getName();//msig.getName(); // 注解所在方法名区分不同的限流策略 //获取rateLimiter if(map.containsKey(functionName)){ rateLimiter = map.get(functionName); }else { map.put(functionName, RateLimiter.create(limitNum)); rateLimiter = map.get(functionName); } try { if (rateLimiter.tryAcquire()) { //执行方法 obj = joinPoint.proceed(); } else { //拒绝了请求(服务降级) return "系统繁忙,稍后再试"; } } catch (Throwable throwable) { } return obj; } }
其它和《AOP+Semaphore实现单节点的接口(方法)限流》差不多。