Redis、別名 Remote Dictionary Serverは、オープンソースのインメモリデータベースです。これはサーバーのRAM上で動作するデータ構造化ストレージシステムであり、最速のソリッドステートドライブ(SSD)よりもはるかに高速です。その結果、Redisは非常に応答性が高く、レート制限に最適です。
レート制限は、ユーザーがサーバーにリソースをリクエストできる回数を制限します。多くのサービスは、ユーザーが過剰な負荷でサーバーを過負荷にしようとするなどのサービス悪用を防ぐためにレート制限を使用しています。例えば、PHPを使用して、Webアプリケーション用の公開API(Application Programming Interface)を開発する場合、レート制限が必要になります。なぜなら、APIを一般に公開する場合、特定の時間内に個人がアクティビティを繰り返すことができる回数を制限したいためです。システムに対する権限を持たないユーザーが、システムを停止させてしまう可能性があります。
レート制限は、設定された制限を超えるユーザーリクエストを拒否することで、アプリケーションをスムーズに機能させます。多数のクライアントがいる場合、レート制限はフェアユースポリシーを適用し、各ユーザーが高速でアプリケーションにアクセスできるようにします。また、レート制限はサーバーの混雑を緩和することで、帯域幅のコストを節約するのにも役立ちます。
ユーザーのアクティビティをMySQLのようなデータベースで追跡することで、レート制限プログラムを作成することは可能です。しかし、そのようなデータはディスクから読み込んで定義された制限と比較して評価する必要があるため、複数の人がシステムにアクセスする場合、最終的な結果はスケーラブルではない可能性があります。これは非効率的であるだけでなく、リレーショナルデータベース管理ソリューションはそのために構築されていません。
Redisはインメモリデータベースとして動作し、この用途において信頼性が証明されているため、レートリミッターを作成するのに適した選択肢です。このチュートリアルでは、Ubuntu 20.04上でRedisを使用してPHPのレート制限を実装する.
始めましょう!
前提条件
このチュートリアルを進めるには、以下が必要です。
-
最新バージョンのUbuntuがインストールされていること(システム上)。
-
システムユーザーがsudo権限.
-
を持っていること。LAMPスタック。
-
これをセットアップするには、LAMPスタックのセットアップ方法チュートリアルに従ってください。
-
-
Redisサーバー。
-
システムにRedisをセットアップするには、Redisサーバーのインストールとセキュリティ対策方法ガイドに従ってください。
-
ステップ1:PHP用Redis拡張機能のインストール
始める前に、パッケージの競合を避けるためにリポジトリを更新しましょう。
|
1 |
sudo apt update |
次に、PHPプログラムでRedisを使用できるようにするパッケージである php-redis拡張機能をインストールします。以下のsudoコマンドを実行して php-redis:
|
1 |
sudo apt install -y php-redis |
その後、Apacheサーバーを再起動して、 php-redisライブラリをロードします。
|
1 |
sudo systemctl restart apache2 |
次のステップは、ソフトウェアインデックスの情報を更新し、PHP用のRedisライブラリをインストールすることです。その後、ユーザーのIPアドレスに基づいてアクセスを制限するPHPリソースを作成します。
ステップ2:レート制限用のPHP Webリソースの作成
このステップでは、 demo.phpファイルをWebサーバーのルートディレクトリ( /var/www/html/)に作成します。このファイルは一般に公開され、ユーザーは好みのWebブラウザでURLを開くことができます。後ほど、 curlコマンドを使用して、使用したいリソースのアクセシビリティを検証します。ユーザーは15秒間に3回、サンプルリソースファイルにアクセスできます。最大制限を超える試行はエラーメッセージをスローします。
このファイルの主な機能は、Redisサーバーに強く依存しています。ファイル内のPHPコードは、ユーザーが初めてリソースにアクセスしたときに、ユーザーのIPアドレスに応じてRedisサーバー上にキーを作成します。ユーザーがリソースに戻ってきたとき、コードはユーザーのIPアドレスをRedisサーバーに保存されているキーと照合し、キーが存在する場合は値を1増やします。PHPコードは、新しい値が最大値に達したかどうかをチェックし続けます。
15秒後、ユーザーのIPアドレスに基づくRedisキーの有効期限が切れ、Webリソースへのユーザーのアクセスの追跡が再開されます。次のファイルを /var/www/html/demo.php ファイルを nano テキストエディタで開きます:
|
1 |
sudo nano /var/www/html/demo.php |
次に、すべてのフィールドに入力してRedisクラスを初期化します。忘れずに REDIS_PASSWORD を正しい値に設定してください:
|
1 2 3 4 |
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->auth('REDIS_PASSWORD'); |
Redis->auth はRedisサーバーのプレーンテキスト認証をサポートしています。これはローカル(localhost経由)で作業している場合はうまく機能しますが、リモートのRedisサーバーを扱う場合は、SSL認証 が推奨されます。
次に、同じファイル内で、以下の変数をデフォルト値に設定します:
|
1 2 3 4 5 |
. . . $max_calls_limit = 3; $time_period = 15; $total_user_calls = 0; |
これらのステートメントについて詳しく理解しましょう:
-
$max_calls_limit:ユーザーはこの最大呼び出し制限を超えてリソースにアクセスすることはできません。
-
$time_period:これは時間枠として使用され、秒単位でカウントされます。ここでは、ユーザーは $max_calls_limit .
-
$total_user_calls:指定された期間内に、ユーザーが calls 制限に対してアクセスを要求した回数を合計します。
次に、Webページへのアクセスを要求しているリクエストのIPアドレスを取得するために、以下のコードを追加します:
|
1 2 3 4 5 6 7 8 9 10 |
. . . if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $user_ip_address = $_SERVER['HTTP_CLIENT_IP']; } elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $user_ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $user_ip_address = $_SERVER['REMOTE_ADDR']; } |
デモンストレーションとして、このコードはユーザーのアクションをIPアドレスごとに記録します。サーバー上に認証が必要な保護されたリソースがある場合は、ユーザー名やアクセストークンを使用してユーザーのアクションを追跡できます。
このガイドでは、システムにログインする各ユーザーに一意の識別子(顧客ID、開発者ID、ベンダーID、またはユーザーIDなど)が割り当てられます。チュートリアルを進める際は、 $user_ip address の代わりにこれらのIDを使用することを忘れないでください。
ここでは、概念を説明するためにユーザーのIPアドレスで十分です。前のコードスニペットからユーザーのIPアドレスを取得したら、ファイルに以下のコードブロックを追加します:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
. . . if (!$redis->exists($user_ip_address)) { $redis->set($user_ip_address, 1); $redis->expire($user_ip_address, $time_period); $total_user_calls = 1; } else { $redis->INCR($user_ip_address); $total_user_calls = $redis->get($user_ip_address); if ($total_user_calls > $max_calls_limit) { echo "ユーザー " . $user_ip_address . " の制限を超えました。"; exit(); } } echo "ようこそ " . $user_ip_address . "、合計呼び出し回数: " . $total_user_calls . "、期間: " . $time_period . " 秒"; |
これらのステートメントの概要は以下の通りです:
-
if...else: このステートメントは、Redisサーバー上にIPアドレスで定義されたキーが存在するかどうかを検証します。
-
キーが見つからない場合、if (!$redis->exists($user_ip_address)) {...}、以下を使用してキーを設定し、その値を1に定義できます。 $redis->set($user_ip_address, 1).
-
-
$redis->expire($user_ip_address, $time_period): 指定された時間にキーの有効期限が切れるようにします。このチュートリアルでは、以下に設定しています。 15 秒。
-
RedisキーにユーザーのIPアドレスが見つからない場合は、変数 $total_user_calls 値 として設定します。 1.
-
else {...}: このステートメントブロックは、 $redis->INCR($user_ip_address); コマンドを使用して、Redisキーの値を以下だけ増やします。 1。これは、キーに関連付けられた各IPアドレスに適用されます。
-
注意: これは、キーがすでにRedisサーバーに設定されており、繰り返しリクエストとしてカウントされている場合にのみ実現できます。
-
-
$total_user_calls = $redis->get($user_ip_address): このステートメントは、Redisサーバー上のそれぞれのIPアドレスベースのキーを検証することにより、総リクエスト数を取得します。
-
if ($total_user_calls > $max_calls_limit) {... }..: この if ステートメントは、制限を超えたかどうかを検証するために使用されます。超えている場合は、以下を使用してユーザーに通知します。 echo "User" . $user_ip_address . " limit exceeded.";.
最後に、指定された期間内の訪問回数をユーザーに通知するために、以下の echo "Welcome" . $user_ip_address . "total calls made" . $total_user_calls . "in" . $time_period . "seconds"; ステートメントを使用します。
その後、以下のコード行を /var/www/html/demo.php ファイルに追加します:
|
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 |
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->auth('REDIS_PASSWORD'); $max_calls_limit = 3; $time_period = 15; $total_user_calls = 0; if (!empty($_SERVER['HTTP_CLIENT_IP'])) { $user_ip_address = $_SERVER['HTTP_CLIENT_IP']; }elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $user_ip_address = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $user_ip_address = $_SERVER['REMOTE_ADDR']; } if (!$redis->exists($user_ip_address)) { $redis->set($user_ip_address, 1); $redis->expire($user_ip_address, $time_period); $total_user_calls = 1; } else { $redis->INCR($user_ip_address); $total_user_calls = $redis->get($user_ip_address); if ($total_user_calls > $max_calls_limit) { echo "User " . $user_ip_address . " limit exceeded."; exit(); } } echo "Welcome " . $user_ip_address . " total calls made " . $total_user_calls . " in " . $time_period . " seconds"; ?> |
修正が完了したら、 /var/www/html/demo.php ファイルを保存して閉じます。これで、 demo.php ウェブページに、ユーザーのレート制限に必要なロジックが作成されました。次のステップでスクリプトをテストしてみましょう。
ステップ 3: Redis レート制限テストの実行
このステップでは、 curl コマンドを使用して、ステップ 2 で作成したウェブリソースをリクエストします。スクリプトを徹底的にテストするには、リソースを5回リクエストする単一のコマンドを実行します。これは、プレースホルダーのURL引数を demo.php ファイルの末尾。実行するには curl 命令を5回実行するには、値 ?[1-5] をリクエストの末尾に使用します。
以下の curl コマンドを実行します:
|
1 |
curl -H "Accept: text/plain" -H "Content-Type: text/plain" -X GET http://localhost/demo.php?[1-5] |
コードを実行すると、以下のような結果が表示されます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[1/5]: http://localhost/demo.php?1 --> <stdout> --_curl_--http://localhost/demo.php?1 Welcome 127.0.0.1 合計 呼び出し 行われた 1 内 15 秒 [2/5]: http://localhost/demo.php?2 --> <stdout> --_curl_--http://localhost/demo.php?2 Welcome 127.0.0.1 合計 呼び出し 行われた 2 内 15 秒 [3/5]: http://localhost/demo.php?3 --> <stdout> --_curl_--http://localhost/demo.php?3 Welcome 127.0.0.1 合計 呼び出し 行われた 3 内 15 秒 [4/5]: http://localhost/demo.php?4 --> <stdout> --_curl_--http://localhost/demo.php?4 ユーザー 127.0.0.1 制限 超過. [5/5]: http://localhost/demo.php?5 --> <stdout> --_curl_--http://localhost/demo.php?5 ユーザー 127.0.0.1 制限 超過. |
ご覧の通り、最初の3つのリクエストは問題なく実行されました。しかし、4番目と5番目のクエリはスクリプトによってレート制限されました。Redisサーバーが、ユーザーがクエリを実行できる速度を制限している可能性が高いです。
このガイドでは、以下に示す2つの変数に低い値を指定しています。
|
1 2 3 4 |
... $max_calls_limit = 3; $time_period = 15; ... |
本番環境でアプリケーションを構築する場合は、想定される利用頻度に応じて、より大きな値を使用することを検討するとよいでしょう。
これらの数値を調整する前に、リアルタイムの統計情報を調査することをお勧めします。この例では、サーバーログに平均的なユーザーが60秒ごとに1,000回アプリケーションにアクセスしていることが示されている場合、その数値をスロットリングの基準として使用できます。
結論
このガイドでは、Ubuntu 20.04上でPHPとRedisサーバーを活用する方法を学びました。この記事ではRedisを使用したレート制限の仕組みを説明していますが、Webアプリケーションの要件に合わせてカスタマイズすることができます。以下のような実際の例を調べることをお勧めします。Twitterの最大リクエスト制限, GoogleのCustom Search JSON APIや、その他の同様のドキュメントを参照してレート制限に関する知識を深め、異なる時間制限を使用して自身で実験してみることをお勧めします。
さらに、当社のブログ:
- Ubuntu 18.04でKubernetesクラスターにPHPアプリケーションをデプロイする
- Ubuntu 20.04でPHPmyadminをインストールしてセキュリティを確保する
- Ubuntu 20.04にLEMPスタック(Linux Nginx MySQL PHP)をインストールする方法
Happy Computing!
コメント
コメントはまだありません。最初のコメントを投稿しましょう。