总之 , 向左移动K个permits所需要花费的节流时间等于长度为K的函数的面积 。假设RateLimiter的storedPermits饱和状态 , 从maxPermits到thresholdPermits就等于warmupPeriod 。从thresholdPermits到0的时间是warmupPeriod/2(原因是为了维护以前的实现 , 其中coldFactor硬编码为3)
计算thresholdPermits和maxPermits的公式:
- 从thresholdPermits到0花费的节流时间等于函数从0到thresholdPermits的积分 , 也就是thresholdPermits*stableIntervals 。也等于warmupPeriod/2 。
thresholdPermits=0.5*warmupPeriod/stableInterval - 从maxPermits到thresholdPermits就是函数从thresholdPermits到maxPermits的积分 , 也就是梯型部分的面积 , 它等于0.5(stableInterval+coldInterval)(maxPermits - thresholdPermits) , 也就是warmupPeriod
maxPermits = thresholdPermits + 2*warmupPeriod/(stableInterval+coldInterval)
这里源码先从SmoothBursty入手 , 首先是RateLimiter类里的创建:
public static RateLimiter create(double permitsPerSecond) { return create(permitsPerSecond, SleepingStopwatch.createFromSystemTimer());}@VisibleForTestingstatic RateLimiter create(double permitsPerSecond, SleepingStopwatch stopwatch) { // maxBurstSeconds代表这个桶能存储的令牌数换算的秒数 , 通过这个秒数就可以知道能存储的令牌数 , 也就表示这个桶的大小 。RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */); rateLimiter.setRate(permitsPerSecond); return rateLimiter;}我们看到外界无法自定义SmoothBursty的桶大小 , 所以我们无论是创建什么速率的RateLimiter , 桶的大小就必然是rate*1的大小 , 那么就有人通过反射的方式在满足自己想要修改桶大小的需求:https://github.com/vipshop/vjtools/commit/9eacb861960df0c41b2323ce14da037a9fdc0629
setRate方法:
public final void setRate(double permitsPerSecond) { checkArgument( permitsPerSecond > 0.0 && !Double.isNaN(permitsPerSecond), "rate must be positive"); // 需要全局同步 synchronized (mutex()) { doSetRate(permitsPerSecond, stopwatch.readMicros()); } } private volatile Object mutexDoNotUseDirectly; // 产生一个同步使用的对象 , 适应双重检查锁保证这个对象是个单例 private Object mutex() { Object mutex = mutexDoNotUseDirectly; if (mutex == null) { synchronized (this) { mutex = mutexDoNotUseDirectly; if (mutex == null) { mutexDoNotUseDirectly = mutex = new Object(); } } } return mutex; }互斥锁来保证线程的安全 , 具体实现代码中使用volatile修饰的mutexDoNotUseDirectly字段和双重校验同步锁来保证生成单例 , 从而保证每次调用mutex()都是锁同一个对象 。这在后续的获取令牌中也需要用到 。
doSetRate方法是子类SmoothRateLimiter实现:
// 当前存储的令牌 double storedPermits; // 最大能够存储的令牌 double maxPermits; // 两次获取令牌的固定间隔时间 double stableIntervalMicros; // 下一个请求能被授予的时间 , 一次请求后这个值就会推后 , 一个大请求推后的时间比一个小请求推后的时间要多 // 这和预消费有关系 , 上一次消费的令牌 private long nextFreeTicketMicros = 0L;final void doSetRate(double permitsPerSecond, long nowMicros) { resync(nowMicros); // 计算出请求授予的间隔时间 double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond; this.stableIntervalMicros = stableIntervalMicros; doSetRate(permitsPerSecond, stableIntervalMicros); } // 更新storedPermits和nextFreeTicketMicros void resync(long nowMicros) { // 当前时间大于下一个请求时间 说明这次请求不用等待 如果不是就不会出现增加令牌的操作 if (nowMicros > nextFreeTicketMicros) { // 根据上次请求和这次请求间隔计算能够增加的令牌 double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros(); // 存储的令牌不能超过maxPermits storedPermits = min(maxPermits, storedPermits + newPermits); // 修改nextFreeTicketMicros为当前时间 nextFreeTicketMicros = nowMicros; } } // 子类实现abstract void doSetRate(double permitsPerSecond, double stableIntervalMicros); // 返回冷却期间需要等待获得新许可证的微秒数 。子类实现 abstract double coolDownIntervalMicros();SmoothBursty的实现
static final class SmoothBursty extends SmoothRateLimiter { /** The work (permits) of how many seconds can be saved up if this RateLimiter is unused? */ final double maxBurstSeconds; SmoothBursty(SleepingStopwatch stopwatch, double maxBurstSeconds) { super(stopwatch); this.maxBurstSeconds = maxBurstSeconds; } @Override void doSetRate(double permitsPerSecond, double stableIntervalMicros) { double oldMaxPermits = this.maxPermits; // 计算出桶的最大值 maxPermits = maxBurstSeconds * permitsPerSecond; if (oldMaxPermits == Double.POSITIVE_INFINITY) { // if we don't special-case this, we would get storedPermits == NaN, below storedPermits = maxPermits; } else { // 初始化为0 后续重新设置时按新maxPermits和老maxPermits的比例计算storedPermits storedPermits = (oldMaxPermits == 0.0) ? 0.0 // initial state : storedPermits * maxPermits / oldMaxPermits; } } @Override long storedPermitsToWaitTime(double storedPermits, double permitsToTake) { return 0L; } // 对于SmoothBursty 没有什么冷却时期 , 所以始终返回的是stableIntervalMicros @Override double coolDownIntervalMicros() { return stableIntervalMicros; } }
推荐阅读
- Java8新特性之空指针异常的克星Optional类
- 跟我一起了解Java到底好在哪?
- 交通事故现场拍照技巧,全是干货!
- 防火墙主备切换案例分析,干货值得收藏
- java服务 tomcat安装,不要太简单
- Java开发必会的Linux命令
- 常用排序算法之JavaScript实现
- Java核心技术-macOS下配置Java11环境
- 分享cmd 窗口中运行 Java 程序小技巧
- 如何理解JAVA类装载器ClassLoader?高级开发才懂的技术点
