Java干货丨限流常规设计和实例( 五 )

总之 , 向左移动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)
【Java干货丨限流常规设计和实例】Guava RateLimiter源码
这里源码先从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; } }


推荐阅读