zoukankan      html  css  js  c++  java
  • RateLimiter 的底层实现是啥?

    作者:温安适

    来源:https://my.oschina.net/floor/blog/4965200

    前言

    本文不是一个RateLimiter的详细分析,仅仅是概要分析。

    令牌桶算法

    一说到RateLimiter,必然要是说的令牌桶,它的大致逻辑如下

    按图实现

    令牌桶的图,网上到处可见,按图实现也非常简单,无非是定时添加令牌桶,并提供一个获取令牌的函数,博主实现了一遍代码如下:

    import java.util.concurrent.*;
    
    public class MyRateLimiter {
        //令牌桶
        BlockingQueue<Integer>TOKEN_BUCKET=new LinkedBlockingDeque<>(5);
    
        public static void main(String[] args) {
            MyRateLimiter myRateLimiter=new MyRateLimiter();
            myRateLimiter.addTokenFixedRate();
           for(int i=0;i<10;i++){
                    myRateLimiter.acqurie();
                    System.out.println("第几次执行i:" + i + ",执行时间为:" + System.currentTimeMillis());
            }
        }
       /**
        * 定时添加令牌
        */
        public void addTokenFixedRate(){
            ScheduledExecutorService scheduledExecutorService= Executors.newSingleThreadScheduledExecutor();
            scheduledExecutorService.scheduleAtFixedRate(()->{
                        boolean suc=TOKEN_BUCKET.offer(1);
                        if(!suc){
                            System.out.println("令牌桶满了丢弃");
                        }
            },0,200,TimeUnit.MILLISECONDS);
        }
    
        public void acqurie(){
            while (TOKEN_BUCKET.poll()==null){};
        }
    
    }
    

    测试结果如下,基本满足要求

    RateLimiter概要实现

    我一开始是按照自己实现的逻辑,去查看Guava的RateLimiter的源码的,结果发现RateLimiter根本没有集合充当桶,核心是记录了下一令牌产生的时间与现存令牌数,并动态更新它们。

    概要逻辑图如下:

    按照这个图看核心代码就比较容易了,摘录核心代码如下:

    @CanIgnoreReturnValue
    public double acquire(int permits) {
      long microsToWait = reserve(permits);
      stopwatch.sleepMicrosUninterruptibly(microsToWait);
      return 1.0 * microsToWait / SECONDS.toMicros(1L);
    }
    //Reserve 一路向下能查到如下代码  reserveEarliestAvailable
    
    final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
        resync(nowMicros);
        long returnValue = nextFreeTicketMicros;
    	// 现存令牌可以提供的令牌数
        double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    	//需要刷新的令牌数
        double freshPermits = requiredPermits - storedPermitsToSpend;
    	//等待时间=需要刷新的令牌数*固定间隔+存储许可等待时间
        long waitMicros =
            storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
                + (long) (freshPermits * stableIntervalMicros);
    	//下次令牌生产时间=本次令牌生产时间+等待时间
        this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
        this.storedPermits -= storedPermitsToSpend;
        return returnValue;
    }
    

    总结:RateLimiter根本没有集合充当桶,核心是记录了下一令牌产生的时间与现存令牌数,并动态更新它们。

    近期热文推荐:

    1.1,000+ 道 Java面试题及答案整理(2021最新版)

    2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!

    3.阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

    4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!

    5.《Java开发手册(嵩山版)》最新发布,速速下载!

    觉得不错,别忘了随手点赞+转发哦!

  • 相关阅读:
    Linux忘了root的密码怎么办
    缩略图的实现
    ASP.NET程序编写注意 (转载)
    太极拳
    Linux系统管理技巧大荟萃
    茶经(转载)
    datagrid的显示控制
    太极功
    Linux下硬盘分区详解
    Tomcat4.0中文问题简单解决方法
  • 原文地址:https://www.cnblogs.com/javastack/p/14864735.html
Copyright © 2011-2022 走看看