インタビュアー:ソースコード解析を-RateLimiter制限について話します

RateLimiter:2つのがあり、実装クラスであるSmoothBurstySmoothWarmingUpトークンバケットアルゴリズムで実装されている変異体は、ことを除いて、SmoothBurstyトークンの追加速度は一定であり、かつSmoothWarmingUpウォームアップ期間のトークンに加えて低速では、ウォームアップ時間を持つことになります一定の速度に達するまでゆっくりと上昇。そのアプリケーションのシナリオは、いくつかのシステムのために小さいだけの最高の状態を達成するために、一定期間後にウォームアップ、QPSを開始耐えることができます。

 

基本的な使用

RateLimiter使用することは非常に簡単:

// 方法はごとに生成着信トークンの数で作成 
RateLimiter rateLimiter RateLimiter.create =(1 ;)
 のためにint型 I = 0;私は<5; Iは++ ){
     // 獲得方法待機不十分なトークンがある場合、プロセスは時間を待つために戻り、必要な着信トークンの数である
    ダブル(1 WAITTIME rateLimiter.acquire = ); 
    のSystem.out.println(のSystem.currentTimeMillis() / 1000+ "" + WAITTIME); 
}

 

次のように出力されます。

1548070953、0.0 
1548070954、0.998356 
1548070955、0.998136 
1548070956、0.99982

 

そこ不十分トークンである場合、なおacquire方法およびこの呼び出しをブロックしないが、ヘッドの次の呼び出しでカウントされます。例えば、しかし最初の呼び出し、トークンバケットとしないトークンは、最初のコールをブロックしていないが、2番目の呼び出しは、1秒遮断します。言い換えれば、トークンの下に各呼び出しは、(トークンバケットが不十分な場合)法案を足に次の呼び出しをすることです。

RateLimiter rateLimiter = RateLimiter.create(1 )。
ダブル WAITTIME = rateLimiter.acquire(1000年); 
System.out.println(のSystem.currentTimeMillis() / 1000 + "" + WAITTIME)。
WAITTIME = rateLimiter.acquire(1 )。
System.out.println(のSystem.currentTimeMillis() / 1000 + "" + WAITTIME)。

 

次のように出力されます。

1548072250、0.0 
1548073250、999.998773

 

この設計の目的は以下のとおりです。

Last, but not least: consider a RateLimiter with rate of 1 permit per second, currently completely unused, and an expensive acquire(100) request comes. It would be nonsensical to just wait for 100 seconds, and /then/ start the actual task. Why wait without doing anything? A much better approach is to /allow/ the request right away (as if it was an acquire(1) request instead), and postpone /subsequent/ requests as needed. In this version, we allow starting the task immediately, and postpone by 100 seconds future requests, thus we allow for work to get done in the meantime instead of waiting idly.

各要求に基づく賃金が不要な待ち時間になる場合だけで、置きます。例えば、毎秒1の速度でトークン増加、最初はトークンバケットは、その要求は100トークンを必要とし、それはタスク待ち100Sを開始する必要がありました。したがって、より良いアプローチは、最初の要求、遅延後にその要求を解放することです。

また、RateLimiterがあるtryAcquire方法は、そうでない場合は、すぐにfalseを返し、すぐに十分なトークン場合にtrueを返します。

ソースコード解析

この記事では、分析SmoothBurstyの実装を。

初めて目にSmoothBurstyいくつかの主要分野の:

// 桶中最多存放多少秒的令牌数
final double maxBurstSeconds;
//桶中的令牌个数
double storedPermits;
//桶中最多能存放多少个令牌,=maxBurstSeconds*每秒生成令牌个数
double maxPermits;
//加入令牌的平均间隔,单位为微秒,如果加入令牌速度为每秒5个,则该值为1000*1000/5
double stableIntervalMicros;
//下一个请求需要等待的时间
private long nextFreeTicketMicros = 0L;

RateLimiter作成

メソッドを作成RateLimiterの作成を見てください。

// permitsPerSecond毎秒トークンの数を生成する
パブリック 静的(作成RateLimiterをダブルpermitsPerSecond){
     リターンを作成(permitsPerSecond、SleepingStopwatch.createFromSystemTimer())を; 
} 

// SleepingStopwatchは、主にタイミングのために使用され、スリープ
静的 RateLimiterは(作成ダブルpermitsPerSecond、SleepingStopwatchをストップウォッチ){
     // SmoothBursty作成 
    rateLimiter rateLimiter = 新新 SmoothBursty(ストップウォッチ、1.0 / * maxBurstSecondsを* / ); 
    rateLimiter.setRate(permitsPerSecond); 
    を返すrateLimiterを。 
}
mainメソッドを作成することはSmoothBurstyインスタンスを作成し、そのsetRateメソッドを呼び出すことです。ここで注意1.にmaxBurstSecondsコード化された 0 

@Override 
最終 ボイド doSetRate(ダブル permitsPerSecond、ロングnowMicros){ 
    リシンク(nowMicros); 
    ダブル stableIntervalMicros = SECONDS.toMicros(1L)/ permitsPerSecond;
     この .stableIntervalMicrosの=のstableIntervalMicros; 
    doSetRate(permitsPerSecond、stableIntervalMicros); 
} 

無効再同期を(ロングnowMicros) {
     // 現在時刻がnextFreeTicketMicrosよりも大きい場合、命令はを待たずに、トークン要求をうまく補完されたこの要求を負う
    IF(nowMicros>nextFreeTicketMicros){
       // トークンを追加するために必要な時間を計算し、coolDownIntervalMicrosはstableIntervalMicros返し
      ダブル nextFreeTicketMicros)/ - newPermits =(nowMicros ; coolDownIntervalMicros()
      // 更新トークンバケット、maxPermits超えない 
      storedPermits =分( maxPermits、storedPermits + newPermits);
       // 最初のセットnowMicros 
      nextFreeTicketMicros = nowMicros; 
    } 
} 

@Override 
無効 doSetRate(ダブル permitsPerSecond、ダブルstableIntervalMicros){
     ダブル oldMaxPermits = この; .maxPermits 
    maxPermitsをMaxBurstSeconds * = permitsPerSecond;
     IF(oldMaxPermits == Double.POSITIVE_INFINITYなど){
         // WEは、特別な場合は、この-ケース、WEは得られないstoredPermits ==うNaN 3を、以下 
        storedPermits = maxPermits; 
    } {
         // 最初の呼び出しoldMaxPermits 0、storedPermits(トークンバケットの数)も0 
        storedPermits = 
                (oldMaxPermits == 0.0  0.0?// 初期状態 
                        :storedPermits * maxPermits / oldMaxPermits; 
    } 
}

 

setRate設定方法maxPermits=maxBurstSeconds * permitsPerSecond、及びmaxBurstSeconds1、ようにmaxBurstSeconds 1秒間に格納されたトークンの数だけ。

ことに注意してくださいSmoothBurstyのみを通じて非publicクラス、というRateLimiter.create作成方法、および方法はmaxBurstSeconds 、我々は唯一のバケットサイズを作成することができることを意味し1.0の死を、書くことであるのpermitsPerSecond * 1 SmoothBurstyもちろん、ない反射の方法で、オブジェクト(スコープ)、グアバのgithubの倉庫にはいくつかの問題(あるissue12号issue3issue4)外部から設定したいmaxBurstSeconds が、役人のリターンを見ていないが。オープンソース・プロジェクトでのみ製品がしますvjtools、それがこの提起された疑問を、学生はグアバの唯一の製品となりますRateLimiterをしている拡大

私は理解していなかったこのデザイングアバについては、明確な友人がある〜次を言うことができます

遠くSmoothBurstyのオブジェクトが作成され、その後、我々はその分析acquire方法を。

方法を取得

public double acquire(int permits) {
    // 计算本次请求需要休眠多久(受上次请求影响)
    long microsToWait = reserve(permits);
    // 开始休眠
    stopwatch.sleepMicrosUninterruptibly(microsToWait);
    return 1.0 * microsToWait / SECONDS.toMicros(1L);
}
 
final long reserve(int permits) {
    checkPermits(permits);
    synchronized (mutex()) {
      return reserveAndGetWaitLength(permits, stopwatch.readMicros());
    }
}

final long reserveAndGetWaitLength(int permits, long nowMicros) {
    long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
    return max(momentAvailable - nowMicros, 0);
}

final long reserveEarliestAvailable(int requiredPermits, long nowMicros) {
    // 这里调用了上面提到的resync方法,可能会更新桶中的令牌值和nextFreeTicketMicros
    resync(nowMicros);
    // 如果上次请求花费的令牌还没有补齐,这里returnValue为上一次请求后需要等待的时间,否则为nowMicros
    long returnValue = nextFreeTicketMicros;
    double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
    // 缺少的令牌数
    double freshPermits = requiredPermits - storedPermitsToSpend;
    // waitMicros为下一次请求需要等待的时间;SmoothBursty的storedPermitsToWaitTime返回0
    long waitMicros =
        storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
            + (long) (freshPermits * stableIntervalMicros);
    // 更新nextFreeTicketMicros
    this.nextFreeTicketMicros = LongMath.saturatedAdd(nextFreeTicketMicros, waitMicros);
    // 减少令牌
    this.storedPermits -= storedPermitsToSpend;
    return returnValue;
}

 

acquire中会调用reserve方法获得当前请求需要等待的时间,然后进行休眠。reserve方法最终会调用到reserveEarliestAvailable,在该方法中会先调用上文提到的resync方法对桶中的令牌进行补充(如果需要的话),然后减少桶中的令牌,以及计算这次请求欠的令牌数及需要等待的时间(由下次请求负责等待)。

如果上一次请求没有欠令牌或欠的令牌已经还清则返回值为nowMicros,否则返回值为上一次请求缺少的令牌个数*生成一个令牌所需要的时间。

End

本文讲解了RateLimiter子类SmoothBursty的源码,对于另一个子类SmoothWarmingUp的原理大家可以自行分析。相对于传统意义上的令牌桶,RateLimiter的实现还是略有不同,主要体现在一次请求的花费由下一次请求来承担这一点上。

本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,需要自己领取。
传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q

おすすめ

転載: www.cnblogs.com/yuxiang1/p/11347015.html