1. 概要
この記事では、GuavaライブラリのRateLimiterクラスについて説明します。
RateLimiter クラスは、何らかの処理が発生する速度を調整できるようにする構造です。 N個の許可を持つRateLimiterを作成する場合、プロセスは1秒あたり最大N個の許可を発行できることを意味します。
2. Mavenの依存関係
Guavaのライブラリを使用します。
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
最新バージョンはここにあります。
3. RateLimiterの作成と使用
doSomeLimitedOperation()の実行速度を1秒あたり2回に制限したいとします。
create()ファクトリメソッドを使用して、RateLimiterインスタンスを作成できます。
RateLimiter rateLimiter = RateLimiter.create(2);
次に、 RateLimiter、から実行許可を取得するには、 acquire()メソッドを呼び出す必要があります。
rateLimiter.acquire(1);
それが機能することを確認するために、throttledメソッドを2回続けて呼び出します。
long startTime = ZonedDateTime.now().getSecond();
rateLimiter.acquire(1);
doSomeLimitedOperation();
rateLimiter.acquire(1);
doSomeLimitedOperation();
long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;
テストを簡素化するために、 doSomeLimitedOperation()メソッドがすぐに完了していると仮定します。
このような場合、 acquire()メソッドの両方の呼び出しがブロックされないようにし、経過時間は1秒未満または1秒未満にする必要があります。両方の許可をすぐに取得できるためです。
assertThat(elapsedTimeSeconds <= 1);
さらに、1回の acquire()呼び出しですべての許可を取得できます。
@Test
public void givenLimitedResource_whenRequestOnce_thenShouldPermitWithoutBlocking() {
// given
RateLimiter rateLimiter = RateLimiter.create(100);
// when
long startTime = ZonedDateTime.now().getSecond();
rateLimiter.acquire(100);
doSomeLimitedOperation();
long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;
// then
assertThat(elapsedTimeSeconds <= 1);
}
これは、たとえば、1秒あたり100バイトを送信する必要がある場合に役立ちます。 一度に1つの許可を取得して1バイトを100回送信できます。 一方、100バイトすべてを一度に送信して、1回の操作で100個すべての許可を取得することができます。
4. 妨害的な方法で許可を取得する
ここで、もう少し複雑な例を考えてみましょう。
100個の許可を持つRateLimiterを作成します。 次に、1000の許可を取得する必要があるアクションを実行します。 RateLimiterの仕様によると、このようなアクションは1秒あたり100ユニットのアクションしか実行できないため、完了するまでに少なくとも10秒かかります。
@Test
public void givenLimitedResource_whenUseRateLimiter_thenShouldLimitPermits() {
// given
RateLimiter rateLimiter = RateLimiter.create(100);
// when
long startTime = ZonedDateTime.now().getSecond();
IntStream.range(0, 1000).forEach(i -> {
rateLimiter.acquire();
doSomeLimitedOperation();
});
long elapsedTimeSeconds = ZonedDateTime.now().getSecond() - startTime;
// then
assertThat(elapsedTimeSeconds >= 10);
}
ここでacquire()メソッドをどのように使用しているかに注意してください。これはブロッキングメソッドであり、使用するときは注意が必要です。 acquire()メソッドが呼び出されると、許可が使用可能になるまで実行中のスレッドをブロックします。
引数なしでacquire()を呼び出すことは、引数として1を使用して呼び出すことと同じです –1つの許可を取得しようとします。
5. タイムアウト付きの許可の取得
The RateLimiter APIも非常に便利です取得() その方法引数としてタイムアウトとTimeUnitを受け入れます。
使用可能な許可がないときにこのメソッドを呼び出すと、タイムアウト内に十分な使用可能な許可がない場合、指定された時間待機してからタイムアウトになります。
指定されたタイムアウト内に使用可能な許可がない場合、falseを返します。 acquire()が成功すると、はtrueを返します。
@Test
public void givenLimitedResource_whenTryAcquire_shouldNotBlockIndefinitely() {
// given
RateLimiter rateLimiter = RateLimiter.create(1);
// when
rateLimiter.acquire();
boolean result = rateLimiter.tryAcquire(2, 10, TimeUnit.MILLISECONDS);
// then
assertThat(result).isFalse();
}
1つの許可でRateLimiterを作成したため、2つの許可を取得しようとすると、常に tryAcquire()がfalseを返します。
6. 結論
このクイックチュートリアルでは、GuavaライブラリからのRateLimiterコンストラクトを確認しました。
RateLimtiter を使用して、1秒あたりの許可数を制限する方法を学びました。 ブロッキングAPIの使用方法を確認し、明示的なタイムアウトを使用して許可を取得しました。
いつものように、これらすべての例とコードスニペットの実装は、 GitHubプロジェクトにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。