Bucket4J と Redis を使用してスケーラブルなレート制限を実装する – ステップバイステップ ガイド
このチュートリアルでは、スケールされたサービスにレート制限を実装する方法を学びます。
Bucket4J ライブラリを使用して実装し、Redis を分散キャッシュとして使用します。
レート制限を使用する理由
レート制限の必要性を理解し、このチュートリアルで使用するツールを紹介するために、いくつかの基本から始めましょう。
無制限料金の問題
Twitter API のようなパブリック API で、ユーザーが 1 時間あたり無制限のリクエストを実行できる場合、次のような問題が発生する可能性があります。
- リソースの枯渇
- サービスの品質の低下
- サービス拒否攻撃
これにより、サービスが利用できないか、 遅くなる状況が発生する可能性があります。 。さらに予期せぬ出費が発生する可能性もあります。 サービスによって発生するものです。
レート制限がどのように役立つか
まず、レート制限によりサービス拒否攻撃を防ぐことができます。レート制限を重複排除メカニズムまたは API キーと組み合わせると、分散型サービス拒否攻撃の防止にも役立ちます。
次に、トラフィックの見積もりに役立ちます。これはパブリック API にとって非常に重要です。これを自動スクリプトと組み合わせて、サービスを監視および拡張することもできます。
そして 3 番目に、これを使用して階層ベースの価格設定を実装できます。このタイプの価格モデルは、ユーザーがより高いレートのリクエストに対して料金を支払うことができることを意味します。 Twitter API はその一例です。
トークン バケット アルゴリズム
トークン バケットは、レート制限を実装するために使用できるアルゴリズムです。つまり、次のように動作します。
<オル>分散システムにトークン バケットを実装する方法
分散システムでトークン バケット アルゴリズムを実装するには、分散キャッシュを使用する必要があります。 .
キャッシュはキーと値のストアです。 バケット情報を保存します。これを実装するには Redis キャッシュを使用します。
Bucket4j を内部的に使用すると、Java JCache API のあらゆる実装をプラグインできます。 Redis の Redisson クライアントが使用する実装です。
プロジェクトの実施
Spring Boot フレームワークを使用してサービスを構築します。
私たちのサービスには以下のコンポーネントが含まれます:
<オル>まず、すべてのリクエストの API をレート制限する方法を学びます。次に、ユーザーごとまたは価格帯ごとに、より複雑なレート制限メカニズムを実装する方法を学びます。
プロジェクトのセットアップから始めましょう。
依存関係をインストールする
以下の依存関係を pom.xml に追加しましょう。 ( またはbuild.gradle ) ファイル。
<dependencies>
<!-- To build the Rest API -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Redisson Starter = Spring Data Redis starter(excluding other clients) and Redisson client -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.17.0</version>
</dependency>
<!-- Bucket4J starter = Bucket4J + JCache -->
<dependency>
<groupId>com.giffing.bucket4j.spring.boot.starter</groupId>
<artifactId>bucket4j-spring-boot-starter</artifactId>
<version>0.5.2</version>
</dependency>
</dependencies>
キャッシュ構成
まず、Redis サーバーを起動する必要があります。ローカル マシンのポート 6379 で Redis サーバーが実行されているとします。
次の 2 つの手順を実行する必要があります。
<オル>Redisson のドキュメントには、これを通常の Java アプリケーションに実装するための簡潔な手順が記載されています。同じ手順を Spring Boot で実装します。
まずはコードを見てみましょう。必要な Bean を作成するには、Configuration クラスを作成する必要があります。
@Configuration
public class RedisConfig {
@Bean
public Config config() {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
return config;
}
@Bean
public CacheManager cacheManager(Config config) {
CacheManager manager = Caching.getCachingProvider().getCacheManager();
cacheManager.createCache("cache", RedissonConfiguration.fromConfig(config));
return cacheManager;
}
@Bean
ProxyManager<String> proxyManager(CacheManager cacheManager) {
return new JCacheProxyManager<>(cacheManager.getCache("cache"));
}
}
これは何をするのですか?
<オル>API を構築する
簡単な REST API を作成してみましょう。
@RestController
public class RateLimitController {
@GetMapping("/user/{id}")
public String getInfo(@PathVariable("id") String id) {
return "Hello " + id;
}
}
URL http://localhost:8080/user/1 で API をヒットした場合 、応答 Hello 1 が返されます。 .
Bucket4J 構成
レート制限を実装するには、Bucket4J を構成する必要があります。ありがたいことに、スターター ライブラリがあるため、定型コードを記述する必要はありません。
またProxyManager Bean も自動的に検出されます。 前のステップで作成し、それを使用してバケットをキャッシュします。
必要なのは、作成した API を中心にこのライブラリを構成することです。
繰り返しますが、これを行うには複数の方法があります。
スターター ライブラリで定義されているプロパティベースの構成を使用できます。
これは、すべてのユーザーまたはすべてのゲスト ユーザーに対するレート制限などの単純な場合に最も便利な方法です。
ただし、各ユーザーのレート制限など、より複雑なものを実装したい場合は、カスタム コードを作成することをお勧めします。
ユーザーごとのレート制限を実装する予定です。各ユーザーのレート制限がデータベースに保存されており、ユーザー ID を使用してクエリできると仮定しましょう。
コードを段階的に書いてみましょう。
バケットを作成する
始める前に、バケットがどのように作成されるかを見てみましょう。
Refill refill = Refill.intervally(10, Duration.ofMinutes(1));
Bandwidth limit = Bandwidth.classic(10, refill);
Bucket bucket = Bucket4j.builder()
.addLimit(limit)
.build();
- 詰め替え – どのくらいの時間が経過するとバケットが補充されます。
- 帯域幅 – バケットが持つ帯域幅の量。基本的に、リクエストは補充期間ごとに行われます。
- バケット – これら 2 つのパラメータを使用して設定されたオブジェクト。さらに、バケット内で利用可能なトークンの数を追跡するためにトークン カウンターを維持します。
これを構成要素として使用して、ユースケースに適したものにするためにいくつかの点を変更しましょう。
ProxyManager を使用してバケットを作成およびキャッシュする
Redis にバケットを保存する目的でプロキシ マネージャーを作成しました。バケットを作成したら、Redis にキャッシュする必要があり、再度作成する必要はありません。
これを実現するために、Bucket4j.builder() を置き換えます。 proxyManager.builder() を使用 。 ProxyManager はバケットをキャッシュし、バケットを再度作成しないように処理します。
ProxyManager のビルダーは 2 つのパラメータ、キーを受け取ります。 バケットがキャッシュされる対象と設定オブジェクト バケットの作成に使用されます。
実装方法を見てみましょう:
@Service
public class RateLimiter {
//autowiring dependencies
public Bucket resolveBucket(String key) {
Supplier<BucketConfiguration> configSupplier = getConfigSupplierForUser(key);
// Does not always create a new bucket, but instead returns the existing one if it exists.
return buckets.builder().build(key, configSupplier);
}
private Supplier<BucketConfiguration> getConfigSupplierForUser(String key) {
User user = userRepository.findById(userId);
Refill refill = Refill.intervally(user.getLimit(), Duration.ofMinutes(1));
Bandwidth limit = Bandwidth.classic(user.getLimit(), refill);
return () -> (BucketConfiguration.builder()
.addLimit(limit)
.build());
}
}
提供されたキーのバケットを返すメソッドを作成しました。次のステップでは、これを使用する方法を見ていきます。
トークンの消費方法とレート制限の設定方法
リクエストが受信されると、関連するバケットからトークンを消費しようとします。
tryConsume() を使用します。 これを行うにはバケットのメソッドを使用します。
@GetMapping("/user/{id}")
public String getInfo(@PathVariable("id") String id) {
// gets the bucket for the user
Bucket bucket = rateLimiter.resolveBucket(id);
// tries to consume a token from the bucket
if (bucket.tryConsume(1)) {
return "Hello " + id;
} else {
return "Rate limit exceeded";
}
}
tryConsume() メソッドは true を返します トークンが正常に消費された場合、または false トークンが消費されなかった場合。
サービスをテストする方法
これは、任意の自動テスト手法を使用してテストできます。たとえば、JUnit を使用できます。 getInfo() を呼び出すテスト ケースを書いてみましょう。 メソッドを複数回実行し、応答が正しいことを確認します。
ID 1 のユーザーがいると仮定します。 10 の制限 1 分あたりのリクエスト数。 ID 2 を持つユーザーもいると仮定しましょう。 20 の制限 1 分あたりのリクエスト数
両方のユーザーに対して 11 件のリクエストがヒットし、ID 1 のユーザーに対してリクエストが失敗することを確認します。 ただし、ID 2 のユーザーの場合は成功します。 .
@Test
public void testGetInfo() {
// calls the method 10 times for user 1
for (int i = 0; i < 10; i++) {
rateLimiter.getInfo(1));
rateLimiter.getInfo(2));
}
// verifies that the response is rate limited for user 1
assertEquals("Rate limit exceeded", rateLimiter.getInfo(1));
// verifies that the response is successful for user 2
assertEquals("Hello 2", rateLimiter.getInfo(2));
}
テストを実行すると、テストが成功したことがわかります。
結論
このチュートリアルでは、Spring Boot アプリケーションで Bucket4j と Redis を使用してレート リミッターを作成する方法について説明しました。また、JCache を使用して Redisson クライアントを設定する方法と、JCache を使用してバケットをキャッシュする方法についても説明しました。
最後に、特定のユーザーのレート制限リクエストに使用できるシンプルなレート リミッターを実装しました。
このチュートリアルを楽しんでいただければ幸いです。読んでいただきありがとうございます!
無料でコーディングを学びましょう。 freeCodeCamp のオープンソース カリキュラムは、40,000 人以上の人々が開発者としての職に就くのに役立ちました。始めましょう
-
Redis PSUBSCRIBE – redis pub/subで複数のパターンをサブスクライブする方法
このチュートリアルでは、redis-cliを使用してredisメッセージブローカーシステムで複数のパターンをサブスクライブする方法について学習します。 PSUBSCRIBEコマンド PSUBSCRIBEコマンドは、指定されたパターンと名前が一致するチャネルに発行されたすべてのメッセージを受信するために、クライアントを1つ以上のパターンにサブスクライブするために使用されます。パターンはglobスタイルで指定されます。 SUBSCRIBEコマンドと同様に、クライアントがpsubscribeコマンドを実行すると、サブスクライブされたパターンをリッスンするサブスクライブ状態になります。サブスクライ
-
Redis と Upstash による OpenAI プロジェクトの強化:ユースケースと将来の機能
最近、多くの開発者が Upstash を OpenAI や他の AI API (Google Cloud AI、IBM Watson など) と統合していることがわかりました。抱き合う顔。この投稿では、最も一般的な使用例について説明し、さらに多くの機能をサポートするための将来の計画について概説します。 レート制限 レート制限は、AI ベースのアプリケーションを管理する上で重要なコンポーネントであり、開発者とユーザーの両方に対する保護手段として機能します。 AI アプリケーションは大量のデータを処理し、複雑な計算を実行するため、大量の計算リソースを必要とします。適切に管理しないと、システムの過