使用RateLimiter+自定义注解实现单机限流

使用RateLimiter+自定义注解实现单机限流

实现步骤

  • 自定义注解
  • 编写aop

自定义注解

首先我们自定义注解然后使用aop去拦截,这样在需要限流的方法上面使用自定义注解就可以完成限流

@Target(ElementType.METHOD)   // 使用范围: 方法
@Retention(RetentionPolicy.RUNTIME)   // 有效范围: 运行时
public @interface MyLimitAnnotation {
}

编写aop

这里用guava的RateLimiter实现单机限流的效果,写的比较简单,如有需要可以自己定制化

@Aspect
@Component
public class RateLimitAop{

    /**
     * QPS 最好使用热加载的方式
     */
    private static final double QPS=1;
    private static RateLimiter LIMITER = RateLimiter.create(QPS);

    @Pointcut("@annotation(com.chen.demo.limiter.MyLimitAnnotation)")
    private void rateLimitPointcut(){

    }

    /**
     * 通知
     * @param joinPoint
     * @return
     */
    @Around("rateLimitPointcut()")
    private void rateLimitAround(ProceedingJoinPoint joinPoint){
        boolean tryAcquire = LIMITER.tryAcquire();
        try {
            if (tryAcquire) {
                System.out.println(String.format("低于%sQPS继续执行",QPS));
                joinPoint.proceed();
            }else {
                System.out.println(String.format("高于%sQPS获取到令牌后执行",QPS));
                LIMITER.acquire();
                joinPoint.proceed();
            }
        }catch (Throwable e){
            e.printStackTrace();
        }
    }

}

测试

配置类

@Configuration
@ComponentScan("com.chen.demo.limiter")
@EnableAspectJAutoProxy
public class LimiterConfig {
}

我们编写一个方法来模拟操作DB

@Component
public class MockDbOperation {
    @MyLimitAnnotation
    public void operationDb() {
        System.out.println("insert data to db");
    }
}

再编写一个测试方法

public class LimitTest {
    @Test
    public void testLimiter(){
        AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(LimiterConfig.class);
        MockDbOperation bean = context.getBean(MockDbOperation.class);
        CompletableFuture.runAsync(
                ()->{
                    for (;;){
                        bean.operationDb();
                        Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MICROSECONDS);
                    }
                }
        , Executors.newFixedThreadPool(5));
        Uninterruptibles.sleepUninterruptibly(10,TimeUnit.SECONDS);
    }
}

最后来看一下执行效果

低于1.0QPS继续执行
insert data to db
高于1.0QPS获取到令牌后执行
insert data to db

最后附上单机限流和分布式限流的注意点

单机限流主要算法有。令牌桶算法,漏桶算法,计数器算法来实现,这里不再赘述。

分布式限流实现原理其实很简单。既然要达到分布式全局限流的效果,那自然需要一个第三方组件来记录请求的次数。其中 Redis 就非常适合这样的场景。

分布式限流实现x

  • 每次请求时将方法名进行md5加密后作为Key 写入到 Redis 中,超时时间设置为 x 秒,Redis 将该 Key 的值进行自增。
  • 当达到阈值时返回错误。
  • 写入 Redis 的操作用 Lua 脚本来完成,利用 Redis 的单线程机制可以保证每个 Redis 请求的原子性

当使用分布式限流时需要注意,因为是集群限流,当集群中的某台服务器宕机后会给其他机器增加压力,如果集群中某个机器又因qps增加宕机,如果不及时处理最后会导致整个服务不可用,并且该服务是下游服务时,上游服务没有做熔断处理有可能会造成服务雪崩

当使用单机限流时,需要注意集群在增加实例时重新调整qps大小

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值