AOP+Semaphore实现单节点的接口(方法)限流

AOP+Semaphore 对项目限流

Semaphore限流是从线程的个数限流。

RateLimiter是从速率限流,目前的算法有漏桶算法和令牌算法。

0、依赖

 

1、自定义注解

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RateLimit {

    /**
     * 资源的名称
     * 
     * @return
     */
    String name() default "";

    /**
     * 资源的key
     *
     * @return
     */
    String key() default "";

    /**
     * Key的prefix
     *
     * @return
     */
    String prefix() default "";

    /**
     * 给定的时间段 单位秒
     *
     * @return
     */
    int period();

    /**
     * 最多的访问限制次数
     *
     * @return
     */
    int limitNum();

    /**
     * 类型
     *
     * @return
     */
    LimitType limitType() default LimitType.CUSTOMER;
public enum LimitType {

    /**
     * 自定义key
     */
    CUSTOMER,
    /**
     * 根据请求者IP
     */
    IP;
    
}

 

2、限流切面

@Component
@Scope
@Aspect
public class ApiRateLimitAspect {
    
    private Logger logger = LoggerFactory.getLogger(ApiRateLimitAspect.class);
    
    //用来存放不同类名$方法名的Semaphore(key为接口名称,value为Semaphore)
    private ConcurrentHashMap<String, Semaphore> map = new ConcurrentHashMap<>();
 
    private Semaphore semaphore;
 
    @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(); //每秒限流的数量
        String functionName = currentMethod.getDeclaringClass().getName() + "#" + currentMethod.getName();//msig.getName(); // 注解所在方法名区分不同的限流策略
 
         if(map.containsKey(functionName)){
             semaphore = map.get(functionName);
         }else {
             map.put(functionName, new Semaphore(limitNum, false));
             semaphore = map.get(functionName);
         }
 
        //方案1:超过阀值时,新请求被阻塞
        try {
            //无参方法tryAcquire()的作用是尝试的获得1个许可,如果获取不到则返回false,该方法通常与if语句结合使用,其具有无阻塞的特点。无阻塞的特点可以使线程不至于在同步处一直持续等待的状态,如果if语句判断不成立则线程会继续走else语句,程序会继续向下运行。
            if (semaphore.tryAcquire()) {
                //执行方法
                obj = joinPoint.proceed();
                
            } else {
                //拒绝了请求(服务降级)
                return "系统繁忙,稍后再试";
            }
        } catch (Throwable throwable) {
            
        } finally {
            //释放一个许可证,默认是1
            semaphore.release();
        }
        
        //方案2:超过阀值时,新请求被阻塞,2选1啊
        try {
            //获取1个许可证,如果没有可用许可证,一直阻塞这里
            semaphore.acquire(1);
            //执行方法
            obj = joinPoint.proceed();
            
        } catch (Throwable throwable) {
            
        } finally {
             //释放一个许可证,默认也是1,会将指定的线程数释放,然后唤醒等待的线程
            semaphore.release();
        }
        return obj;
    }
 
}

 

3、使用注解

@Slf4j
@Controller
public class HelloController {
    @RateLimit(5)
    @RequestMapping("/hello")
    @ResponseBody
    public String hello(){
        //假设业务逻辑执行了x秒
       sleep();
        return "hello";
    }

4、测试:jmeter调用

 

参考:https://blog.csdn.net/weixin_42412601/article/details/105166850

posted on 2015-05-09 09:09  duanxz  阅读(522)  评论(0编辑  收藏  举报