Abstract

システムの安定運用に関するツールということで今回気になり調べてみました。

レートリミッターは、システムの過負荷を防ぎ、安定したパフォーマンスを維持するためのツールです。一定の時間内に処理できるリクエストやタスクの数を制限することで、システムの信頼性と可用性を向上させることに寄与します。

1. Introduction

レートリミッターは、システムのパフォーマンスと安定性を保つために欠かせないツールです。主な役割と必要性は以下の通りです。

  • 過負荷防止
    • システムが過剰なリクエストを処理しきれずにダウンするのを防ぎます。特定の時間内に許可されるリクエスト数を制限することで、システムリソースの負荷を軽減します。
  • 公平性の確保
    • 全てのユーザーに対して公平なリソース配分を保証します。特定のユーザーがリソースを独占するのを防ぎ、全てのユーザーに均等なアクセス機会を提供します。
  • セキュリティ向上
    • DDoS攻撃などの悪意あるリクエストからシステムを保護します。レートリミッターは、異常なリクエストパターンを検出して対処することで、システムの安全性を高めます。

2. What is a rate limiter?

レートリミッターは、特定の時間枠内で許可されるリクエストやタスクの数を制限する仕組みです。これにより、システムの負荷を管理し、安定したパフォーマンスを維持します。

2.1. How it works

  • リクエストの計測
    • レートリミッターは、一定時間内に発生するリクエストの数をカウントします。
  • 制限の適用
    • 許可されたリクエスト数を超えた場合、新しいリクエストは制限され、エラーが返されるか、待機状態に置かれます。
  • バースト制限
    • 短期間に多くのリクエストが集中する場合でも、一時的に許可されるリクエスト数(バースト)が設定されていることがあります。

3. Examples of rate limiter usage

  • サーバーアプリケーション
    • アプリケーションコード内でレートリミッターを実装し、リクエストのレートを制御します。これにより、特定のサービスやリソースへの負荷を管理します。
    • 例えば、Go言語のgolang.org/x/time/rateパッケージを使用して、1秒あたりのリクエスト数やバーストサイズを設定します。
  • クラウドアーキテクチャ
    • クラウドサービス(例:Google Cloud Tasks)でレートリミッターを設定し、バックエンドへのタスク送信レートを制御します。これにより、リソースの効率的な使用と安定性を確保します。
    • 例えば、Cloud Tasksで1秒あたり10タスクに制限する設定を行います。

3.1. Example of use in a server application

3.1.1. How to implement a rate limiter in the Go language

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func main() {
	rateLimiterTest()
}

func rateLimiterTest() {
	http.Handle("/ping", rateLimiter(endpointHandler))
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Println("There was an error listening on port :8080", err)
	}
}

func endpointHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "application/json")
	writer.WriteHeader(http.StatusOK)
	message := "Hello! You've reached the API. How can I assist you today?"
	err := json.NewEncoder(writer).Encode(&message)
	if err != nil {
		return
	}
}

// ★ レートリミッターのコーディングのポイントとなる関数
// limiterが許可した場合のみリクエストを処理できる
func rateLimiter(next func(w http.ResponseWriter, r *http.Request)) http.Handler {
	limiter := rate.NewLimiter(2, 4) // 1秒あたり2リクエスト、バーストサイズ4
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if limiter.Allow() {
			next(w, r)
		} else {
			message := "The API is currently at full capacity. Please try again later."
			w.WriteHeader(http.StatusTooManyRequests)
			json.NewEncoder(w).Encode(&message)
			return
		}
	})
}

3.1.2. Execution results and their explanations

  • 正常な応答
    • 1秒あたり2リクエストまでは正常に処理されます。
  • バースト対応
    • 一時的に最大4リクエストまで許可されますが、その後はレートが制限されます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 正常な応答
takuto-n@MacBook-Air goapp % for i in {1..2}; do curl http://localhost:8080/ping; done
"Hello! You've reached the API. How can I assist you today?"
"Hello! You've reached the API. How can I assist you today?"

# バースト対応
takuto-n@MacBook-Air goapp % for i in {1..2}; do curl http://localhost:8080/ping; done
## 最初の4件はバースト対応で問題なく処理できている。
"Hello! You've reached the API. How can I assist you today?"
"Hello! You've reached the API. How can I assist you today?"
"Hello! You've reached the API. How can I assist you today?"
"Hello! You've reached the API. How can I assist you today?"
## しかし、その後の2件に関しては制限されているので処理されていない。
"The API is currently at full capacity. Please try again later."
"The API is currently at full capacity. Please try again later."

3.2. Example of use in cloud configuration

Google CloudではCloud Tasksを利用することでレートリミッターを実現できます。

大まかな処理の流れは以下の通りです。

  1. タスク作成のアプリケーションがタスクをCloud Tasksに送る
  2. Cloud Tasksは設定されたタスクを管理して、設定に従いタスクハンドラーに配信(dispatch)する
  3. タスクハンドラーは処理を実行する

Cloud Pub/Sub vs Cloud Tasks
Cloud Pub/Sub では publisher と subscriber を分離してアーキテクチャを疎結合にすることが前提とされていることに対し、Cloud Tasks は subscriber を明示的に呼び出し、subscriber による処理を制御できることがポイントとなっています。
配信タイミング・レートを管理することも可能です。

4. Remarks

4.1. Best Practices for Rate Limiters

以下のようなプラクティスを組み合わせることで、効率的なリクエスト制御と管理、堅牢なリトライ戦略の実装、そしてモニタリングとアラート設定による運用の迅速な改善が可能となります。これにより、サービスの安定性と可用性を更に向上させることができます。

4.1.1. Customize error messages and set response codes

  • カスタムエラーメッセージ
    • リクエスト制限が適用された際に、クライアントにわかりやすいエラーメッセージを返すことが重要です。これにより、クライアントが理解しやすく、適切な対処を行えるようになります。
  • レスポンスコードの設定
    • HTTPステータスコードを適切に設定することで、クライアントがリクエストが失敗した理由を正確に把握できるようにします。例えば、429 Too Many Requestsを使用してリクエスト制限が適用されたことを示します。

4.1.2. Queuing and throttling implementation

  • キューイング
    • リクエストをキューに入れて順番に処理することで、サーバーへの負荷を調整し、一定のレートで処理を行うことができます。
  • スロットリング
    • クライアントごとに定められたレート制限を設定することで、過剰なリクエストがサーバーに届かないようにします。これにより、サーバーの過負荷を防ぎます。

4.1.3. Use of API Keys

  • APIキーの発行と管理
    • APIキーを使用して、クライアントごとに異なる制限やクォータを設定することができます。これにより、異なる利用者やアプリケーションに対して個別に制御を行えます。

4.1.4. Metrics and Monitoring

  • メトリクスの収集と可視化
    • リクエストの数、レスポンス時間、エラー率などのメトリクスを収集し、ダッシュボードで可視化することで、サービスのパフォーマンスや状態を把握しやすくします。
  • アラートの設定
    • メトリクスが特定のしきい値を超えた際に自動的にアラートを発行し、問題を迅速に検知して対応することができます。

4.1.5. Appropriate use of cache

  • レスポンスのキャッシュ
    • 頻繁にリクエストされるデータやレスポンスをキャッシュすることで、サーバーの負荷を軽減し、応答時間を短縮することができます。キャッシュを使用する際は、適切な期限を設定することが重要です。

5. Reference