自定义starter实现接口或方法限流功能

本文的思路是利用AOP技术+自定义注解实现对特定的方法或接口进行限流。目前通过查阅相关资料,整理出三种类型限流方法,分别为基于guava限流实现、基于sentinel限流实现、基于Semaphore的实现

一、限流常用的算法

1.1令牌桶算法

令牌桶算法是目前应用最为广泛的限流算法,顾名思义,它有以下两个关键角色。
令牌 :获取到令牌的Request才会被处理,其他Requests要么排队要么被直接丢弃;
桶 :用来装令牌的地方,所有Request都从这个桶里面获取令牌。
令牌桶主要涉及到2个过程,即令牌的生成,令牌的获取。
在这里插入图片描述

1.2漏桶算法

漏桶算法的前半段和令牌桶类似,但是操作的对象不同,结合下图进行理解。令牌桶是将令牌放入桶里,而漏桶是将访问请求的数据包放到桶里。同样的是,如果桶满了,那么后面新来的数据包将被丢弃。
在这里插入图片描述

1.3滑动时间窗口

根据下图,简单描述下滑动时间窗口这种过程:
在这里插入图片描述
黑色大框为时间窗口,可以设定窗口时间单位为5秒,它会随着时间推移向后滑动。我们将窗口内的时间划分为五个小格子,每个格子代表1秒钟,同时这个格子还包含一个计数器,用来计算在当前时间内访问的请求数量。那么这个时间窗口内的总访问量就是所有格子计数器累加后的数值;
比如说,我们在每一秒内有5个用户访问,第5秒内有10个用户访问,那么在0到5秒这个时间窗口内访问量就是15。如果我们的接口设置了时间窗口内访问上限是20,那么当时间到第六秒的时候,这个时间窗口内的计数总和就变成了10,因为1秒的格子已经退出了时间窗口,因此在第六秒内可以接收的访问量就是20-10=10个。

滑动窗口其实也是一种计算器算法,它有一个显著特点,当时间窗口的跨度越长时,限流效果就越平滑。打个比方,如果当前时间窗口只有两秒,而访问请求全部集中在第一秒的时候,当时间向后滑动一秒后,当前窗口的计数量将发生较大的变化,拉长时间窗口可以降低这种情况的发生概率。

二、常用限流方案的实现

2.1基于guava限流实现

guava为谷歌开源的一个比较实用的组件,利用这个组件可以帮助开发人员完成常规的限流操作。其限流工具类——RateLimiter,通过使用该工具类来实现限流功能,RateLimiter是基于“令牌通算法”来实现限流的。

2.1.1引入guava依赖

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

2.1.2TokenBucketLimiter自定义限流注解

    /**
 * 基于guava的令牌桶
 */
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TokenBucketLimiter {
   
   
    int value() default 50;
}

2.1.3GuavaLimiterAop切面

@Aspect
@Component
@Slf4j
public class GuavaLimiterAop {
   
   

    private final Map<String, RateLimiter> rateLimiters = new ConcurrentHashMap<String, RateLimiter>();

    @Pointcut("@annotation(com.mylimit.annotation.TokenBucketLimiter)")
    public void aspect() {
   
   
    }

    @Around(value = "aspect()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
   
   
        log.debug("准备限流");
        Object target = point.getTarget();
        String targetName = target.getClass().getName();
        String methodName = point.getSignature().getName();
        Object[] arguments = point.getArgs();
        Class<?> targetClass = Class.forName(targetName);
        Class<?>[] argTypes = ReflectUtils.getClasses(arguments);
        Method method = targetClass.getDeclaredMethod(methodName, argTypes);
        // 获取目标method上的限流注解@Limiter
        TokenBucketLimiter limiter = method.getAnnotation(TokenBucketLimiter.class);
        RateLimiter rateLimiter;
        Object result = null;
        if (null != limiter) {
   
   
            // 以 class + method + parameters为key,避免重载、重写带来的混乱
            String
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值