ISUCON本の内容をまとめてみた リバースプロキシ nginx編

# はじめに
この記事は[isucon本](https://www.amazon.co.jp/%E9%81%94%E4%BA%BA%E3%81%8C%E6%95%99%E3%81%88%E3%82%8BWeb%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E3%83%81%E3%83%A5%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-%E3%80%9CISUCON%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6%E9%AB%98%E9%80%9F%E5%8C%96%E3%81%AE%E5%AE%9F%E8%B7%B5-%E8%97%A4%E5%8E%9F-%E4%BF%8A%E4%B8%80%E9%83%8E-ebook/dp/B0B1Z9ZMY6/ref=sr_1_1?crid=3VFKVU9V3LQ4D&dib=eyJ2IjoiMSJ9.Fqg1BYtrQnEUkAI-Nz64Vlht-4b2gmnfCNzmBcYOOLrGjHj071QN20LucGBJIEps.ihawrCdi4UxmgZ_Xbpwz0kJimoBykcQ7nS9WIqRl40U&dib_tag=se&keywords=isucon&qid=1732981401&sprefix=isucon%2Caps%2C178&sr=8-1)の6章の内容をまとめたものになります。要点を抑えている(つもり)なので是非ご覧ください

# リバースプロキシの役割
リバースプロキシはクライアントとアプリケーションサーバーの間に配置するサーバーのことです。
```mermaid
graph LR
    Client[クライアント] -->|リクエスト| ReverseProxy[リバースプロキシ]
    ReverseProxy -->|リクエスト転送| AppServer1[アプリケーションサーバー1]
    ReverseProxy -->|リクエスト転送| AppServer2[アプリケーションサーバー2]
    AppServer1 -->|レスポンス| ReverseProxy
    AppServer2 -->|レスポンス| ReverseProxy
    ReverseProxy -->|レスポンス| Client
```
主に以下の役割があります。
## 負荷分散（ロードバランス）

アプリケーションサーバーがある場合、リクエストを振り分けるロードバランサーの役割を果たします。これによって負荷分散が可能になり、大量のリクエストにも対応しやすくなります。

## コンテンツのキャッシュ
画像・CSS・JavaScriptなどの静的ファイルに対して、アプリケーション側で処理をする必要はありません。ですので、リバースプロキシで直接静的ファイルを返した方がパフォーマンスを向上させることができます。

## HTTPS通信の終端
リバースプロキシがない場合、回線が細いなどの理由で遅いクライアントにレスポンスを返す際にリソースが占有され、処理が遅くなってしまいます。

```mermaid
graph TD
    Client[クライアント]
    AppServer[アプリケーションサーバー]
    Internet[回線（細い/遅い）]

    Client -->|リクエスト| AppServer
    AppServer -->|レスポンス| Client
    Client -.->|遅い通信| Internet
    Internet -.->|遅延| AppServer
```
リバースプロキシがあればリバースプロキシがレスポンスを返してくれるため、遅いクライアントとの通信でアプリケーションサーバーのプロセスが専有されなくなります。アプリケーションサーバーは、安定した通信ができるリバースプロキシに対してレスポンスを返せばよいので処理効率がよくなります。
```mermaid
graph TD
    Client[クライアント]
    ReverseProxy[リバースプロキシ]
    AppServer[アプリケーションサーバー]
    Internet[回線（細い/遅い）]

    Client -->|リクエスト| ReverseProxy
    ReverseProxy -->|リクエスト| AppServer
    AppServer -->|レスポンス| ReverseProxy
    ReverseProxy -->|レスポンス| Client
    Client -.->|遅い通信| Internet
    Internet -.->|遅延| ReverseProxy
```

# プロセスとスレッド
リクエストを処理するアーキテクチャとして大きく二つのアーキテクチャがあります。
## シングルプロセス・マルチスレッド
一つのプロセス内で複数のスレッドをもちいて処理をするアーキテクチャーです。全てのリクエストを一つのプロセスで対応し、複数のスレッドで対応します。異なるスレッド同士ではリソースを共有できるため、メモリが少なくて済みます。
## マルチプロセス・シングルスレッド
複数のプロセスで動作しますがスレッドが一つで処理をするアーキテクチャです。一リクエストに対して一プロセスで対応します。プロセス間ではリソースを共有できないため、メモリ消費量が大きくなります。
## C10K問題
C10K問題とはクライアントが一万を超えた段階でパフォーマンスが大きく低下するという問題のことです。マルチプロセス・シングルスレッドでは仕様できるプロセス数に限りがあります。

このとき、プロセスを切り替えるために**コンテキスト・スイッチ**が必要になります。コンテキスト・スイッチではCPUが現在実行しているプロセスの状態を保存し、新しいプロセスの状態を読み込みます。リクエストが多いとコンテキスト・スイッチの実行回数が増え、パフォーマンスが大きく落ちてしまいます。

```mermaid
sequenceDiagram
    participant Client as クライアント
    participant Process1 as プロセス1
    participant Process2 as プロセス2
    participant CPU as CPU
    

    Client->>Process1: リクエスト1
    Note over Process1: プロセス1で処理
    Process1->>CPU: コンテキスト保存
    CPU-->>Process2: 切り替え
    Client->>Process2: リクエスト2
    Note over Process2: プロセス2で処理
    Process2->>CPU: コンテキスト保存
    Process1->>CPU: コンテキスト保存
    CPU-->>Process2: 切り替え
    Process2->>CPU: コンテキスト保存
    CPU-->>Process1: 切り替え
    Note over CPU: 大量の負荷が発生！！

```

シングルプロセス・マルチスレッドのアーキテクチャでは一プロセスでリクエストを処理するので効率よく大量のリクエストを捌くことができます。しかしこのアーキテクチャでもスレッドの切り替えにコンテキスト・スイッチを実行するので1スレッドがレスポンスを返すまで占有されるアーキテクチャにすると先程と同様にC10K問題が発生します。

```mermaid
sequenceDiagram
    participant Client as クライアント
    participant Process1 as プロセス
    participant CPU as CPU
    

    Client->>Process1: リクエスト1
    Note over Process1: プロセス1で処理
    Process1->>CPU:処理を依頼
    Client->>Process1: リクエスト2
    Note over Process1: プロセス2で処理
    Process1->>CPU:処理を依頼
    Note over CPU: プロセスのコンテキストスイッチが発生しない
```

# nginxとは
nginxはリバースプロキシとして利用される代表的なソフトウェアであり、C10K問題を解決するために開発されました。マルチプロセス・シングルスレッドのアーキテクチャのため、一見C10K問題に弱そうに見えますが**ノンブロッキングI/O**と**多重I/O**を活用し、**イベント駆動**でリクエスト・レスポンスを扱うことで同時並行に大量のリクエストを捌くことができます。

## ノンブロッキングI/O
ノンブロッキングI/OとはI/O処理を非同期的に行うことです。通常のブロッキングI/OではI/O処理をする際に処理をしていないのにプロセスやスレッドを占有されてしまいます。
```mermaid
sequenceDiagram
    participant Client as クライアント
    participant Application as アプリケーション
    participant IO as I/Oデバイス

    Client->>+Application: リクエスト送信
    Application->>IO: I/O操作を開始
    Note over Application: I/O待ちでブロック中
    IO-->>Application: I/O処理完了
    Application-->>-Client: レスポンス送信
```
しかし、ノンブロッキングI/Oではブロッキングが行われないため効率的に処理を行うことができます。
```mermaid
sequenceDiagram
    participant Client as クライアント
    participant Application as アプリケーション
    participant IO as I/Oデバイス

    Client->>Application: リクエスト送信
    Application->>IO: I/O操作を開始
    Note right of Application: 別の処理を実行可能
    Application->>Application: 別の処理
    IO-->>Application: I/O処理完了を通知
    Application-->>Client: レスポンス送信
```

## 多重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編](https://qiita.com/Atkiyama/items/e9429910a17fa749e243)
[ISUCON本の内容をまとめてみた リバースプロキシ nginx編](https://qiita.com/Atkiyama/items/2dcfa7a3147368da6cec)
[ISUCON本の内容をまとめてみた キャッシュ編](https://qiita.com/Atkiyama/items/91a1c2924f15f29264cc)
[ISUCON本の内容をまとめてみた 高速化に必要なその他技術編](https://qiita.com/Atkiyama/items/8de9f6e5db5c6a4384c4)
[ISUCON本の内容をまとめてみた OS Linux編](https://qiita.com/Atkiyama/items/32bdc11ea631fe1c1820)