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实现单节点的接口(方法)限流》差不多。

 

posted on 2015-05-11 10:58  duanxz  阅读(3006)  评论(0编辑  收藏  举报