はじめに
Webサーバーを使うときに、NginxとApacheって言葉よく耳にしますよね。
ただその違いっていまいち良くわからなくないですか??(僕だけだったらすみません笑)
ちなみに僕は「Nginxの方がなんか人気だし良さそうな気がする。。。」という何の根拠もないイメージしか持っていませんでした。
さすがにもう少しちゃんと理解したいと思い、今回はこの違いを記事にしてみました。
ぜひぜひ最後まで読んでみてください〜!
※ちなみにNginxとApacheはそれぞれ、「エンジンエックス」「アパッチ」と読みます。
そもそもNginxとApacheとは?
NginxとApacheはWebサーバーのソフトです。
サーバーにこれらのソフトをインストールすると、Webサーバーとしての機能をもちます。
Webサーバーソフトにおいて、2021年12月時点でNginxとApacheが圧倒的シェアを誇っています。
※引用:『TECH+ マイナビニュース 12月Webサーバシェア、Apacheが増加』
結論、NginxとApacheの違いは
Nginxの方がApacheよりも同時大量アクセスに対処できます。
C10K問題という問題がポイントになってきます。
C10K問題はググってみると以下のように書いています。
「ハードウェアの性能上は問題がなくても,あまりにもクライアントの数が多くなるとサーバがパンクする問題のこと」をC10K問題と定義しています。
引用:『技術評論者ブログ C10K問題』
CPUやメモリの性能によらず、クライアント数が多くなってしまうと、Webサーバーの処理が追いつかなくなってしまうんですね。
なぜこのようなことが起きてしまうのか理解するために、Webサーバーの処理を深堀りしていきます。
そもそもプログラムはどのように実行されるのか
Webサーバーはプログラムで動いているので、まずはプログラムがサーバー上でどのように実行されているかを簡単に説明していきます。
プログラムが実行されるとき、まずはメモリにその処理内容が書き込まれます。この処理内容をプロセスと呼びます。
またプロセスをCPUの実行単位まで細かくしたものをスレッドと呼びます。
CPUには処理を実行するコアというものがあり、1コアにつき1度に処理できるスレッド数は1つです(以下図参照)。
上の図ではスレッド数が少ないので処理を問題なく捌けそうですが、処理が多くなると以下の図のように処理待ちのプロセスやスレッドが多発してしまいます。
プロセスやスレッドが多くなると、何が問題か
プロセスやスレッドが多くなってしまうと、以下のような問題が出てきてしまいます。
- プロセスの上限に達してしまう
- ファイルディスクリプタ※の上限の上限に達してしまう
- コンテキストスイッチ※にオーバーヘッドが発生し、負荷が大きくなる
補足
※ファイルディスクリプタ
=> プログラムからファイルを操作する際に、操作対象のファイルを識別・同定するために割り当てられる番号。
※コンテキストスイッチ
=> CPUが処理するプロセスを切り返る作業。複数のプロセスを処理する際にCPUの状態(コンテキスト)を保存したり復元したりする。この時CPUは何も処理できないので、コンテキストスイッチが多発するとオーバーヘッドが発生してしまう。
上記のような問題によってC10K問題は起きてしまいます。
ApacheとNginxに話を戻すと、Apacheはプロセスやスレッドが多くなりC10K問題を抱えてしまうのですが、Nginxはこの部分をうまく回避しています。
ということで、次にApachとNginxの処理の違いを見ていこうと思います。
Apacheはアクセスをどのように処理するのか
Apacheの処理は大きく2つあります。
『preforkモデル』『workerモデル』です。
※ イベント駆動モデルというのもありますが、Apacheのイベント駆動モデルはworkerモデルに近いためここでは割愛します。
preforkモデルもworkerモデルも、コントローラープロセスという大元のプロセスがあります。
コントローラープロセスの子プロセスとして、リクエストを処理するプロセスがあります。
preforkモデルは複数のプロセスを発行しますが、1プロセスに対して1スレッドになります。
workerモデルは1プロセスに対して複数のスレッドを発行します。
ちなみに、Webサーバーの場合のプロセスやスレッドは以下のようなイメージです。
preforkモデルもworkerモデルもアクセスが来るたびにプロセスやスレッドを発行して処理します。
アクセスが増えるたびにプロセスやスレッドの数が増えてしまうんですね。
Apacheの処理がC10K問題を引き起こしそうな気がしてきましたよね。
preforkモデルとworkerモデルについて、もう少し詳しく見ていきます。
まず、preforkモデルの処理から見ていきましょう。
1プロセスあたり、1スレッドなのでリクエストの分だけプロセスの発行が行われます。
プロセスの最大数※は32bit Linuxでは32768, 64bit Linuxでは4Mですので、この分リクエストがくると処理がパンクしてしまいます。
またCPUが複数のプロセスを処理するために、コンテキストスイッチが多発してしまいます。
※プロセスの最大数について、記載した数値はLinuxのpid(プロセスのid, 一意な値をもつ)の最大数となります。他のプロセスもあるので、実際には記載した数値よりも許容するプロセス数は少なくなると思います。
上記の問題は、workerモデル(マルチスレッド)になると多少は軽減されます。
workerモデルはスレッドがプロセスに対して複数あるので、発行されるプロセス数が減ります。プロセスが減る分、少なくともプロセスの上限には達しにくくなりま。また同じプロセスのスレッドはメモリ空間が共有されているので、メモリ空間切り替えの負担が軽減されます。
ただし、ファイルディスクリプタの上限に達しやすくなってしまいます。
ファイルディスクリプタの上限は1プロセスあたり、1,024です。
ということで、preforkモデルもworkerモデルも大量にアクセスがくるとC10K問題が起きる可能性がありそうなことがわかってきました。
ここまでで、Apacheの処理を見てきたので、次にNginxの処理を見ていきます。
Nginxはアクセスをどのように処理するのか
Nginxは1つのプロセスでいくつものアクセスを捌くのが特徴です(以下図参照)。
マスタープロセスの下にいくつかの子プロセス(ワーカープロセス)があり、各ワーカープロセスで複数のアクセスを捌いています。
この点がApacheと大きく異なる点ですね。
このような処理を可能にしているのはノンブロッキングI/Oとイベント駆動という仕組みです。
※ NginxのI/Oの仕組みにはもう一つ非同期I/Oがありますが、本記事ではノンブロッキングI/Oを記載します。
ノンブロッキングI/Oは処理をブロックしないI/Oのことを言います(そのままですね笑)
これだけだとよくわからないと思うので、まずはブロッキングI/Oの処理において、プロセスが実行されるときの流れを見ていきます。
- プロセスがブロック状態になる(カーネルからシステムコールの返答が返ってくるまで)
- カーネルがI/Oを開始
- I/Oが完了したらカーネルは結果を返す
- 処理完了&ブロック解除
要はカーネルがI/Oをしている間、プロセスは待ち状態になって無駄な時間が発生してしまいます(以下の図参照)。
※ 引用元:『Boost application performance using asynchronous I/O』
ノンブロッキングI/Oの場合は、ブロックしません。
カーネルにシステムコールをしたら他の処理に移ってしまいます。
定期的にカーネルに処理が完了したか問い合わせて、終わっていなければエラー、終わっていれば結果が返ってきます(下図参照)。
※ 引用元:『Boost application performance using asynchronous I/O』
こうすることで、カーネルのI/Oの時間を他の処理に割り当てることができますし、非常に効率的な処理を実行することができます。
さて、Nginxに話を戻しますね!
前述した通りNginxは、ノンブロッキングI/Oとイベント駆動という仕組みをもっています。
ノンブロッキングI/Oは上述の通り、ブロックの処理がないI/Oでした。
イベント駆動について、(Nginxの場合は)クライアントからのアクセスをイベントとして扱い、それをきっかけにプロセス内で処理を実行することを言います。
例えば、2つのアクセス(アクセス①,アクセス②)が来たとします。
※アクセス①とアクセス②はほぼ同時だが、アクセス①の方が少し早くきたとします。
まずアクセス①がくると、プロセス内で処理が走ります。
今回の場合は、まずaccept()をしていきます。
通常であれば、accept()の処理が完了するまでブロックされてしまうのですが、Nginxの場合は特にブロックされることはありません。
そのため、直後にきたアクセス②のaccept()の処理も実行を開始することができます。
read()やwrite()の処理も同様に、ブロックされずにアクセス①とアクセス②をほぼ同時に処理することができます。
以上の特徴からNginxは1つのプロセスでいくつものアクセスを捌くことができて、大量アクセスにも対応可能になっています。
おわり
NginxとApacheの違いをざっくり見ていきました。
ポイントはアクセスに対する処理の違いでしたね。
Apacheはアクセスが増加すると、プロセスも増加してしまいますが、Nginxはアクセスが増加してもプロセス数は一定でした。
このような特徴からNginxは大量アクセスも捌け、C10K問題を起こしません。
これだけ書くと、Nginxの方が良いように思えますが、Apacheにもメリットはあります。
例えばApacheのpreforkモデルは、PHPも動かせます。そのためできるだけ早く環境を立ち上げたい等の時はApacheの方が人によっては良いかもしれません。
どちらもメリット・デメリットがあるので、自分の状況に合わせて使い分けていきたいですね!
最後まで読んでいただきありがとうございました!
参考文献
書籍
Webサイト
- Context Switch(コンテキストスイッチ)とは?
- 【Linux】プロセス番号の最大値 Linux カーネルパラメータ
- 【必見】ファイルディスクリプタ設定でLinuxをチューニング
- いまさら聞けないNode.js
- C10K問題
- HTTP Serverのプロセス構造(UNIX版)
- プロセスの生成
- ノンブロッキングI/Oと非同期I/Oの違いを理解する
- Boost application performance using asynchronous I/O
- Apacheコミッターが見た、Apache vs nginx
- 【図解/apache】MPM prefork/worker /event (イベント駆動)の違い~CPUコアの使い方~
- Nginxのアーキテクチャを理解する
- Nginxが早い理由について調べた(基礎)