Open5
ISUCON 本の読書メモ(リバースプロキシ)
そもそもプロキシとは?
- クライアントとサーバーの中間に位置
- クライアントからのリクエストを受け取ってサーバーに送る
- proxy = 代理
リバースプロキシとは?
- ユーザーのリクエストを受け取り、そのリクエストをアップストリームサーバーに送る
役割
- 負荷分散(ロードバランス)
- アップストリームサーバーに指定するサーバー台数を増やすことでアプリケーションサーバー1台当たりのアクセス量を減らせる
- 逆に一部のサーバーに偏らせることも可能
- コンテンツのキャッシュ
- HTTPS 通信の終端(これ何?)
他にも…
- HTTP ヘッダーの書き換え
- IP アドレスを使用したアクセス制御
- ロギング
本章で学ぶこと
- 転送時のデータ圧縮
- リクエストとレスポンスのバッファリング
- リバースプロキシとアップストリームサーバーのコネクション管理
プロセス・スレッド
マルチプロセス・シングルスレッド
- PHP や unicorn(Ruby)など
- クライアントからの1リクエストを1プロセスが処理
- そのプロセスはその処理を行っている間に別のリクエストを処理できない
- そのため、プロセス数と同時に処理できるリクエスト数が一致する
- 1プロセスのメモリ消費量は数十 MB から数百 MB なので、1台のサーバーに大量のプロセスを起動できない
- 例えば 100MB × 100プロセスで 10GB のメモリが必要になり、難しい
- 同時に大量のリクエストが来る場合は、CPU が処理するプロセスを切り替えるためにコンテキストスイッチが発生
- CPU 上のキャッシュを新しいプロセス用に切り替える必要がある
- クライアント数が1万を超えたあたりでパフォーマンスが極端に落ちる(C10K 問題)
- 異なるプロセスではメモリを共有できないため、使用するメモリが多くなる
シングルプロセス・マルチスレッド
- 同じプロセス上のメモリをスレッド同士で共有できる
- スレッドを切り替える場合もコンテキストスイッチが発生する(C10K 問題が同様に発生する)
C10K 問題の回避手法(Go の goroutine)
- 軽量スレッド
- スレッドよりも低コストに並行処理を実装可能
- 並行処理:複数のタスクを同時に進行しているように見える処理方法
- 並列処理:複数のタスクが物理的に同時に実行される(マルチコア)
- Go のランタイムが CPU のコア数 n 分のスレッドを作成
- m 個の goroutine を n 個のスレッドで実行するため、m:n スケジューリングで実行タイミングや実行スレッドを決定
- スレッドのことを気にせず並行処理を実装できる
❓ Kotlin Coroutines と Go の goroutine はどう違う?
↑ のとおり、クライアントとアプリケーションサーバーを直接結ぶのは非効率
そのため、nginx などのリバースプロキシを間に置くと良い
nginx もマルチプロセス・シングルスレッドだが、イベント駆動というアプローチで C10K 問題を解決している
↑ これがリバースプロキシを置くメリット
- 遅いクライアント(回線が細いなど)にレスポンスを返すときも、リバースプロキシがレスポンスを返すのでアプリケーションサーバーのプロセスが占有されない
- アプリケーションサーバーは安定した通信でリバースプロキシに対してレスポンスを返せば良い
- 少数のリバースプロキシから多数のアプリケーションサーバーにロードバランスすることで、大量のリクエストを受け付けられる
❓ AWS のロードバランサーもリバースプロキシ?
❓ ELB(Elastic Load Balancing)以外にサービスある?
❓ ALB(Application Load Balancer)・NLB(Network Load Balancer)・GLB(Gateway Load Balancer)の違いは?
画像・CSS・JavaScript などの静的ファイルはアプリケーション側の処理は不要
- アプリケーションサーバーではなく、リバースプロキシで配信する方がパフォーマンスが上がる
💡 リバースプロキシを活用することで、マルチプロセス・シングルスレッドのアプリケーションの問題を軽減できる