限流

本文介绍使用guava RateLimiter类实现接口限流。先阐述常见限流算法,如漏桶算法和令牌桶算法,接着说明Guava RateLimiter基于令牌桶算法的原理,还给出其应用示例,包括封装服务类、接口获取令牌等,最后介绍主要API,如引入依赖、自定义注解等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

限流==保险丝策略,可借助框架如spring cloud中Hystrix组件实现。今天介绍使用guava RateLimiter 类实现接口限流。
常见限流算法

漏桶算法

     漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.

     两个变量,一个是桶的大小即支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate),伪代码如下:

     
        double rate; // leak rate in calls/s
        double burst; // bucket size in calls
        long refreshTime; // time for last water refresh
        double water; // water count at refreshTime
     
        refreshWater() {
            long now = getTimeOfDay();
            //水随着时间流逝,不断流走,最多就流干到0.
            water = max(0, water - (now - refreshTime) * rate);
            refreshTime = now;
        }
     
        bool permissionGranted() {
            refreshWater();
            if (water < burst) {
                // 水桶还没满,继续加1
                water++;
                return true;
            }
            return false;
        }

令牌桶算法

     令牌桶算法(Token Bucket)和 Leaky Bucket 效果一样但方向相反,更容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务.

摘自:限流算法之漏桶算法、令牌桶算法
Guava RateLimiter 原理

基于令牌桶算法实现,以固定的频率(1秒)向桶中放入一定数量令牌,比如你希望自己的应用程序QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里扔1000个令牌。业务在每次响应请求之前都从桶中获取令牌,只有取到令牌的请求才会被成功响应。获取的方式有两种:阻塞等待令牌或者取不到立即返回失败

非阻塞:立刻返回或doSomethingElse,不等待令牌

    If(limiter.tryAcquire()){ //未请求到limiter则立即返回false
        doSomething();
    }else{
        doSomethingElse();
    }

阻塞:一直等待获取令牌

     
        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);
            }
        }

Guava RateLimiter 应用

1、封装限流服务类

    @Service
    public class AccessLimitService {
     
        //每秒只发出5个令牌 ,超过permits会被阻塞
        //类似于JDK的信号量Semphore,用来限制对资源并发访问的线程数
        RateLimiter rateLimiter = RateLimiter.create(5.0);
     
        /**
         * 尝试获取令牌
         * @return
         */
        public boolean tryAcquire(){
            return rateLimiter.tryAcquire();
        }
    }

2、限流接口执行业务代码前,先尝试获取令牌

        public String access(){
            //尝试获取令牌
            if(accessLimitService.tryAcquire()){
                //模拟业务执行500毫秒
                try {
                    //dosth
                    Thread.sleep(500);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                return "access success";
            }
                return "access limited";
        }

Guava RateLimiter 主要API


---------------------
作者:钟艾伶
来源:优快云
原文:https://blog.youkuaiyun.com/daybreak1209/article/details/82349244
版权声明:本文为博主原创文章,转载请附上博文链接!

 

1、引入依赖

<!-- guava 限流 -->
<dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>25.1-jre</version>
</dependency>

  

2、自定义注解

@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
public  @interface ServiceLimit {
     String description()  default "";
}

  

3、AOP实现类

@Component
@Scope
@Aspect
public class LimitAspect {
    ////每秒只发出5个令牌,此处是单进程服务的限流,内部采用令牌捅算法实现
    private static   RateLimiter rateLimiter = RateLimiter.create(5.0);
    
    //Service层切点  限流
    @Pointcut("@annotation(com.itstyle.seckill.common.aop.ServiceLimit)")  
    public void ServiceAspect() {
        
    }
    
    @Around("ServiceAspect()")
    public  Object around(ProceedingJoinPoint joinPoint) {
        Boolean flag = rateLimiter.tryAcquire();
        Object obj = null;
        try {
            if(flag){
                obj = joinPoint.proceed();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return obj;
    }
}

 

4、使用

        @Override
    @ServiceLimit
    @Transactional
    public Result startSeckil(long seckillId,long userId) {
        //todo 操作
    }

 


---------------------
作者:yellow_han
来源:优快云
原文:https://blog.youkuaiyun.com/u014769528/article/details/84526363
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值