随笔 - 2228  文章 - 4  评论 - 371  阅读 - 1108万

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   duanxz  阅读(530)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
历史上的今天:
2013-05-09 ES之十:ElasticSearch监控工具 - cerebro
2013-05-09 ES之十一:elasticsearch之Routing及restClient API中如何使用route
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示