Nginxのアーキテクチャを理解する

Nginxのアーキテクチャについて調べてたことをまとめた。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/191919/9a623cee-5d97-469f-7e09-770e3ff4199d.png)


# 用語のおさらい

## プロセス（Process）
- **プログラムの実行単位**であり、CPU時間単位で割り振られる。
- 状態(ステート)があり、現在処理中であるRunning状態だったり、実行可能状態であるReady等が存在する。
- CPUがプロセスを実行する場合、そのプロセスが持つメモリデータに対して演算を行う。
- プロセスはテキストセグメントとデータセグメントからなる構造データをメモリ上に持っている。
    - **テキストセグメント**
        - プログラムの命令列
    - **データセグメント**
        - PDA (Processor Data Area)と呼ばれる、プロセッサの情報やプロセス管理用のデータ領域
        - データ領域と呼ばれる、定数等が置かれる静的領域と、通常の変数等が置かれるヒープ領域からなる領域
        - スタック領域と呼ばれる、一時的なデータ保管領域

実行中のプロセスを確認

```
~ ❯❯❯ ps
  PID TTY           TIME CMD
 6966 ttys000    0:00.03 /Applications/iTerm.app/Contents/MacOS/iTerm2 --server login -fp kenta
 6968 ttys000    0:00.35 -zsh
 7116 ttys001    0:00.01 -zsh
```

## スレッド（Thread）
- **CPUを利用するための実行単位で、最小処理単位の概念**。
- `子プロセスは独自の仮想メモリを割り当てられる`ため、より軽量な実行単位が求められLinux2.4からつくられた。

## 並行と並列の違い
定義
> システムが複数の動作（処理の流れ）を同時に実行状態(in progress)に保てる機能を備えている場合を 並行(concurrent)と言い、複数の動作を同時に実行できる場合を並列(parallel)と言います。 重要な概念、違いは「実行状態」という点です。

### 並行(concurrent)
- ある任意時間時で一つの仕事しか行わないが、複数の仕事を切り替えることにより同時に実行すること。
- 複数の仕事（スレッド）を実行状態として保てている状態。

### 並列（pararell）
- ある任意時間時で複数の仕事を同時に実行すること。
- **並列は並行を包含している**ことに注意。

### JavaScriptで例えてみる
非同期処理はメインのスレッドと別スレッドで動いているから並列処理では？と思いがちだが、実際は平行処理を行なっている。

- 具体的に、基本的にJSはシングルスレッドのプログラムなので**一見並列で動いていそうなsetTimeout等の非同期処理も実は同じスレッドで動いている**。
- キューに登録された関数が順番に一つづつ実行されるが、非同期処理の実装では実行待ちのキューにコールバック関数が割り込まれるようになっている。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/191919/53c348f3-0535-2262-4c40-8eb54b1152e2.png)
引用 : https://qiita.com/klme_u6/items/ea155f82cbe44d6f5d88


## シングルプロセスとマルチプロセス（マルチタスク）
### マルチプロセス
- 複数のプロセスを**並列処理**すること。
- **クライアントからの接続毎にプロセスをフォークして処理**する。
- プロセスをフォーク（fork）し、子プロセスを複数立ち上げることで同じプログラムを並列実行することができる。
- プロセス ∋ スレッドの概念であることに注意。
- メリット
    - **メモリ空間が子プロセス毎に独立**しているため、スクリプト言語等を組み込みやすい。
- デメリット
    - メモリ空間が共有できないため、プロセス間のメモリを直接参照できない。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/191919/3be64ae9-0d46-cc7a-6742-bd4cdd8064e7.png)
引用 : https://moro-archive.hatenablog.com/entry/2014/09/11/013520

### シングルプロセス
- そのまま、1つのプロセスでのプログラム実行を示す。
- プロセスは一つなので、**並行処理**となる

## シングルスレッドとマルチスレッド
## マルチスレッド
- 複数のスレッドを**並列処理**すること
- つまり、1プロセスの中で各スレッドの並列処理を行うことができるということ。
- **クライアントからの接続毎にスレッドを生成して処理**する。
- メリット
    - **メモリ空間が各スレッドの間で共有できる**。
    - マルチプロセスモデルに比べ、メモリ空間の切り替えがない分、メモリ消費量や*[コンテキストスイッチ](https://qiita.com/kamihork/items/296ee689a8d48c2bebcd#コンテキストスイッチ)*のオーバーヘッドが小さくなる。
- デメリット
    - メモリ空間を共有しているため、他のスレッドに依存する場合がありスクリプト言語等が組み込めない場合もある。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/191919/33537fd6-c310-ce15-d10c-5293ad2000a0.png)
引用 : https://moro-archive.hatenablog.com/entry/2014/09/11/013520

### シングルスレッド
- 並行して複数のプログラム流れが実行されるマルチスレッドと対比して、プログラムの処理の流れが一本のみの状態のこと。
- 並列処理ではなく、単一の命令の流れを逐次的に実行していく**並行処理**を指す
- 1つのプロセスが1つのスレッドしか持っていない場合、この時はスレッド=プロセスとも言える。
- 例えば、[Node.jsではシングルスレッドが適応](https://qiita.com/kamihork/items/cc7d01195b1a5d283bba)されており、「ファイルI/Oの非同期」と「イベントループ」によって効率よく処理を行うことを可能にしている。

## コンテキストスイッチ
同時接続数分のプロセスやスレッドが必要になり、その数が多くなるとコンテキストスイッチが発生し処理が遅くなる場合がある。
> コンテキストスイッチとは、コンピュータの処理装置（CPU）が現在実行している処理の流れ（プロセス、スレッド）を一時停止し、別のものに切り替えて実行を再開すること。

## C10K問題
> 「C」は「Client(クライアント)」、「10K」は「1万台」を表し、(ハードウェアやネットワーク性能に問題がなくても)同時クライアント接続数がある一定数を超えるとサーバがパンクしてしまう問題のこと。

## アーキテクチャの種類
### preforkとworker
- MPM（Multi Processing Module）の種類。
- Webブラウザからのリクエストの処理部分をモジュール化したもののこと。

#### preforkモデル
- Apacheのデフォルトアーキテクチャで利用されている。
- **マルチプロセスモデル**。
- スレッドを使わず、先行してforkを行なうwebサーバを指す（なので、pre + fork）。
- **リクエストとプロセスが1対1対応で処理**する。
- メリット
    - 子プロセスが何らかの原因で停止しても、他の子プロセスには影響を及ぼすことが無く通信を継続できるため、安定した通信を行える。
- デメリット
    - 同時接続数（クライアント数）に比例して子プロセスの数も増えるので、使用メモリ量やCPU負荷が比例的に増大していくことになり、**C10K問題**に繋がる。

![名称未設定.001.jpeg](https://qiita-image-store.s3.amazonaws.com/0/191919/f133726a-aef7-8c66-a368-bbd6ce2d03fb.jpeg)

#### workerモデル
- Apacheのアーキテクチャとして利用可能。
- **マルチプロセスとマルチスレッドのハイブリッド型モデル**。
- Apacheの子プロセス1つ1つがマルチスレッドで動作し、スレッド1つが1つのクライアントの処理を担う。
- **リクエストとスレッドが1対1対応で処理**する。
- メリット
    - 多くの子プロセスを起動せずに、メモリの使用量を減らすことができる。
- デメリット
    - 同時接続数（クライアント数）に比例してスレッドの数も増えるので、使用メモリ量やCPU負荷が比例的に増大していくことになり、**C10K問題**に繋がる。

![名称未設定.002.jpeg](https://qiita-image-store.s3.amazonaws.com/0/191919/16ed09f3-3adc-d213-da63-4a4f99055a8b.jpeg)

### イベント駆動モデル
- Nginxのアーキテクチャで利用されている。
- イベント駆動型モデルとは、**クライアントのアクセスをイベントとして扱い、それをトリガーにプロセス内で処理を行う**こと。
- 1プロセスは1スレッドしか利用しないので、CPUの数だけプロセスを用意するワーカーモデルが利用されている。
    - Node.jsと同様、**イベントループ方式**（シングルスレッドでループ処理をまわし、キューに溜まったイベントを処理していく処理方式）が利用されている。
- メリット
    - シングルスレッドのプロセスのためメモリ空間の共有が可能。
    - 同時接続数（クライアント数）が増えても、プロセス数やスレッド数は増えないため、**C10K問題を解決**できる。
        - そのため、メモリ消費量やコンテキストスイッチのオーバーヘッドが小さい。
- デメリット
    - メモリ空間を共有しているため、他のスレッドに依存する場合がありスクリプト言語等が組み込めない場合もある。

# Nginxで用いられるアーキテクチャ

## イベント駆動による非同期的なI/Oによるアーキテクチャ
接続ごとにプロセスやスレッドを立ち上げてコンテキストスイッチが起こらないように、Nginxはシングルスレッドで処理を行う。

## プロセスの仕組み

### マスタープロセス
- workerプロセスの立ち上げや管理を担い、**nginx全体のプロセスを制御**する

### ワーカープロセス
- workerと呼ばれる**シングルスレッドのプロセスが、それぞれ複数のリクエストを処理**する。
- つまり、1つのworkerプロセスが、複数のリクエストを処理する。
- ただしワーカプロセス自体はマルチプロセスとして扱うことも可能。各ワーカプロセスはマスタプロセスが受信したシグナルの種類に応じて各ワーカプロセスにもシグナルを送る。

![名称未設定.003.jpeg](https://qiita-image-store.s3.amazonaws.com/0/191919/159a2605-0d8c-f542-1133-a7b500864f13.jpeg)

では、シングルスレッドにおける１プロセスでどうやって、多数のクライアントからのアクセスをさばいているのか？
1プロセスで多数のクライアントの接続を処理するためには**マルチプロセス, マルチスレッド, 非同期I/O, I/Oの多重化**という方法がある。

### I/Oの種類と概要
#### ブロッキングI/OとノンブロッキングI/O
**ブロッキングI/O**・・・データ処理が完了するまで待つこと/操作する前に待ちが発生するという性質。
**ノンブロッキングI/O**・・・データ処理の完了を待たずに他の処理を行うこと/操作する前に待ちが発生しそうなら即座に関数から返るという性質。
#### 同期I/Oと非同期I/O
**同期I/O**・・・データ処理の入出力が可能になった時点で通知する/操作が終わるまでブロックするという性質。
**非同期I/O**・・・データ処理の入出力が完了した時点で通知/IO操作を発効したスレッド自体がブロックせずに別のCPU処理を行えるという性質。
#### I/Oの多重化
2つ以上のI/Oに対して、どれかが入出力可能になった場合の通知をカーネルに依頼する機能。
I/Oの多重化を実現するためのシステムコール「select, poll, epoll, kqueue」と呼ばれるものがあり、これらはそれぞれ以下の処理を行う

- 一つもしくは複数のディスクリプタを監視
- ディスクリプタの準備状態（例 : ready（読み書き可能））になるまで待つ
- 準備が整ったディスクリプタの通知

ファイルディスクリプタ
> ファイルディスクリプタとは、プログラムからファイルを操作する際、操作対象のファイルを識別・同定するために割り当てられる番号。OSにアクセスを依頼する際にファイルを指定するのに用いられる整数値である

**NginxでのI/O処理では、
（イベントループによる）非同期なイベント駆動と、IOの多重化によって、workerプロセスにてリクエストが完全に処理されることを待たずに処理を捌くことができている。**

## Nginxはリバースプロキシとしても利用できる
### 一般的なプロキシ
- クライアントからプロキシサーバに、任意のWebサーバへのリクエストを行う。

### リバースプロキシ
- Webサーバーの代理の役割を行う。
- Webサーバーへのリクエストの代理受信や、クライアントへのレスポンスの代理送信ができる。

### リバースプロキシのメリット
#### キャッシュや圧縮による高速化
- リバースプロキシにてデータのキャッシングや圧縮をすることが可能。
- 同じページへアクセスがあった時は外部のサーバへアクセスを行わずに済むため、応答性能の向上や通信量の削減になる。

#### セキュリティの向上
- IPアドレスでのフィルタリングにより、悪意のあるホストからのリクエストの遮断や特定のIPのみ許可することができる。
- webサーバは直接インターネットに接する必要がないためリスクが低減する。
- リバースプロキシ上でファイアウォールなどのセキュリティ対策をすることで、Webサーバに直接手を加えることなく安全性を高めることができる。

#### SSLの集中管理
- クライアントとリバースプロキシ間の通信路がSSLで暗号化される場合、リバースプロキシが通信内容を復号することで
- 通信するWebサーバー自体をそれぞれHTTPSに対応させることなく、通信路を暗号化することが可能。

#### 負荷分散
- webサーバは複数台で構成され、1台のリバースプロキシサーバから複数のWebサーバにリクエストを振り分け負荷分散が可能。
- また、リバースプロキシでURLを分解してからURLを変換しWEBサーバーに転送ができ、URLの短縮も行うことができる。

#### メモリ効率の向上
- URLから静的コンテンツ/動的コンテンツを各サーバーに振り分けて処理を行うことができ、全体としてメモリ使用効率が上がる。

## Apacheとの比較
[ApacheとNginxについて比較](https://qiita.com/kamihork/items/49e2a363da7d840a4149)を参照。

# 参考文献
[Linuxプロセスとカーネル読解のとっかかり](https://qiita.com/ftakao2007/items/d0fbecd3873054f603f5)
[プロセスとスレッド - Linux Kernel](http://d.hatena.ne.jp/m-bird/20090604/1244165412)
[並行処理、並列処理のあれこれ](https://qiita.com/Kohei909Otsuka/items/26be74de803d195b37bd)
[プロセスとスレッドの違いとは？](https://yakst.com/ja/posts/39)
[イケてるエンジニアになろうシリーズ 〜メモリとプロセスとスレッド編〜](https://moro-archive.hatenablog.com/entry/2014/09/11/013520)
[Nginxの仕組みについて入門](https://blog.mosuke.tech/entry/2016/06/04/180122/)
[preforkとworker](https://www.superweibu.com/archives/606.html)
[Apache MPMとはなんぞやという話](https://norikone.hatenablog.com/entry/2016/01/21/Apache2.4%E3%81%AEMPM%E5%91%A8%E3%82%8A)
[リバースプロキシって何？触りだけ学んだサーバー/インフラ入門](https://qiita.com/growsic/items/fead30272a5fa374ac7b)
[リバースプロキシ (reverse proxy)とは](https://wa3.i-3-i.info/word1755.html)
[プロキシサーバ・リバースプロキシサーバとは？](http://ramenchahan.hatenablog.com/entry/2018/03/20/155344)
[90秒の動画で学ぶITキーワード:リバースプロキシ](https://www.atmarkit.co.jp/ait/articles/1608/25/news034.html)
[Nginxが早い理由について調べた（基礎）](https://blog.framinal.life/entry/2015/09/13/195121)
[コンビニでわかるノンブロッキングIO](https://takezoe.hatenablog.com/entry/2014/10/12/000000)
[ノンブロッキングI/Oと非同期I/Oの違いを理解する](https://blog.takanabe.tokyo/2015/03/26/240/)
[非同期I/OやノンブロッキングI/O及びI/Oの多重化について](http://blog.matsumoto-r.jp/?p=2030)
[ファイルディスクリプタ](http://e-words.jp/w/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%82%BF.html)
[Apacheコミッターが見た、Apache vs nginx](https://openstandia.jp/pdf/140228_osc_seminar_ssof8.pdf)
[【Nginx】Nginxについて(概要)](http://t-tsuyoshi.hatenablog.com/entry/2017/07/29/171133)
[非同期 IO について](https://qiita.com/legokichi/items/1f3b1bd51e206ffdd2a6)
[非同期とノンブロッキングとあと何か](https://keens.github.io/blog/2017/05/19/hidoukitononburokkingutoatonanika/)
[JavaScriptの非同期処理を並列処理と勘違いしていませんか？](https://qiita.com/klme_u6/items/ea155f82cbe44d6f5d88)