はじめに
この記事はisucon本の6章の内容をまとめたものになります。要点を抑えている(つもり)なので是非ご覧ください
リバースプロキシの役割
リバースプロキシはクライアントとアプリケーションサーバーの間に配置するサーバーのことです。
主に以下の役割があります。
負荷分散(ロードバランス)
アプリケーションサーバーがある場合、リクエストを振り分けるロードバランサーの役割を果たします。これによって負荷分散が可能になり、大量のリクエストにも対応しやすくなります。
コンテンツのキャッシュ
画像・CSS・JavaScriptなどの静的ファイルに対して、アプリケーション側で処理をする必要はありません。ですので、リバースプロキシで直接静的ファイルを返した方がパフォーマンスを向上させることができます。
HTTPS通信の終端
リバースプロキシがない場合、回線が細いなどの理由で遅いクライアントにレスポンスを返す際にリソースが占有され、処理が遅くなってしまいます。
リバースプロキシがあればリバースプロキシがレスポンスを返してくれるため、遅いクライアントとの通信でアプリケーションサーバーのプロセスが専有されなくなります。アプリケーションサーバーは、安定した通信ができるリバースプロキシに対してレスポンスを返せばよいので処理効率がよくなります。
プロセスとスレッド
リクエストを処理するアーキテクチャとして大きく二つのアーキテクチャがあります。
シングルプロセス・マルチスレッド
一つのプロセス内で複数のスレッドをもちいて処理をするアーキテクチャーです。全てのリクエストを一つのプロセスで対応し、複数のスレッドで対応します。異なるスレッド同士ではリソースを共有できるため、メモリが少なくて済みます。
マルチプロセス・シングルスレッド
複数のプロセスで動作しますがスレッドが一つで処理をするアーキテクチャです。一リクエストに対して一プロセスで対応します。プロセス間ではリソースを共有できないため、メモリ消費量が大きくなります。
C10K問題
C10K問題とはクライアントが一万を超えた段階でパフォーマンスが大きく低下するという問題のことです。マルチプロセス・シングルスレッドでは仕様できるプロセス数に限りがあります。
このとき、プロセスを切り替えるためにコンテキスト・スイッチが必要になります。コンテキスト・スイッチではCPUが現在実行しているプロセスの状態を保存し、新しいプロセスの状態を読み込みます。リクエストが多いとコンテキスト・スイッチの実行回数が増え、パフォーマンスが大きく落ちてしまいます。
シングルプロセス・マルチスレッドのアーキテクチャでは一プロセスでリクエストを処理するので効率よく大量のリクエストを捌くことができます。しかしこのアーキテクチャでもスレッドの切り替えにコンテキスト・スイッチを実行するので1スレッドがレスポンスを返すまで占有されるアーキテクチャにすると先程と同様にC10K問題が発生します。
nginxとは
nginxはリバースプロキシとして利用される代表的なソフトウェアであり、C10K問題を解決するために開発されました。マルチプロセス・シングルスレッドのアーキテクチャのため、一見C10K問題に弱そうに見えますがノンブロッキングI/Oと多重I/Oを活用し、イベント駆動でリクエスト・レスポンスを扱うことで同時並行に大量のリクエストを捌くことができます。
ノンブロッキングI/O
ノンブロッキングI/OとはI/O処理を非同期的に行うことです。通常のブロッキングI/OではI/O処理をする際に処理をしていないのにプロセスやスレッドを占有されてしまいます。
しかし、ノンブロッキングI/Oではブロッキングが行われないため効率的に処理を行うことができます。
多重I/O
一つのプロセスやスレッドにおいて複数のI/Oチャネルを管理することによってI/Oを効率的に管理することです。適切にチャネルを切り替えることによって効率的な処理が期待できます。
イベント駆動
イベントが起こったことによって動作する設計手法です。nginxのようなアーキテクチャにおいてはイベントを受け取ることでプロセスやスレッドを切り替えることによって効率的な処理をすることができます。
gzipを用いた圧縮
nginxではgzipに対応したHTTPクライアントからのリクエストに対してgzipで圧縮したレスポンスを用いることで効率的な通信を行うことができます。このときの注意点としては以下の二つです。
適切な圧縮レベルの選択
圧縮レベルとは圧縮させる度合いのことで高ければ高いほど容量が小さくなります。一方で高レベルで圧縮したファイルは解凍にも時間がかかるので適切なレベルを選択する必要があります。
アプリケーションサーバー間との通信でも圧縮を行う
クライアントとの通信だけでなくアプリケーションサーバー間との通信でもgzipでの圧縮通信が可能ですので是非とも活用しましょう。
nginxによるリクエスト・レスポンスのバッファリング
nginxはバッファリングを行うことによって遅いクライアントからの通信があったとしてもアプリケーションサーバーのリソースを占有せずに処理を行うことができます。バッファリングとは一時的に通信の内容をファイル等にI/Oする仕組みです。場合によってはI/Oが負荷になるケースもあるので、必ずしも有効にしたらいいわけではないので注意が必要です。
nginxとアップストリームサーバーのコネクション管理
デフォルトの設定ではコネクションを一度使ったら切って再接続するようになっています。パフォーマンスの都合上、コネクションは使いまわした方がいいので適切な設定が必要になります。
nginxのTLS通信を高速にする
nginxのTLS通信を高速化するにはいくつか方法があります
用いる暗号化方式を変更する
用いる暗号化方式によってかかる負荷や暗号の強度が違います。ですので適切な暗号化方式を選択することは処理の高速化に繋がります。書籍では最も利用されているRSA鍵よりも強度が高く処理も高速なECDSA鍵が紹介されいます。また、nginxは複数の鍵を同時に指定できるため、クライアント次第ではRSA鍵とECDSA鍵を同時に用いることもできます。
HTTP/2を利用する
HTTP/2は従来に比べて様々な改善が行われており、その中にはパフォーマンスに関するものもあり高速化に用いることができます。
TLSのバージョンアップ
TLSもバージョンによって高速化されているバージョンもあります。書籍では当時最も使われていたバージョン1.2よりバージョン1.3のほうが高速化されていることが紹介されています。
まとめ
以上がisucon本6章まとめです。チューニングに必須な項目を要約したので是非ご活用ください!
その他の章の記事はこちらから
ISUCON本の内容をまとめてみた DB編
ISUCON本の内容をまとめてみた リバースプロキシ nginx編
ISUCON本の内容をまとめてみた キャッシュ編
ISUCON本の内容をまとめてみた 高速化に必要なその他技術編
ISUCON本の内容をまとめてみた OS Linux編