こんばんは、ぎんです。
記事を読んでいて出てきたこの説明が意味不明すぎたので、今回はNginxについて調べてみることにします。
Nginxが大量のリクエストでも同時に高速処理できるのは、イベント駆動方式を採用しているためです。
Webサーバーのように同時に複数の処理を行うには、ディスクやネットワークといったI/Oの多重化が必要になります。I/O多重化の実装には「select」や「poll」といったシステムコールが従来使われています。
色々調べた結果を書きますが、間違っていたら教えていただけると助かります。
イベント駆動方式について
まずは、イベント駆動方式についてです。
これネットで調べるとプログラミングのイベント駆動式(イベントドリブン)の話がたくさん出てくるので1回忘れます。(ロジック的には同じです)
イベント駆動型の反対が、フロー制御型とかいうのも忘れます。
ApacheとNginxの比較をすると分かりやすいです。
これらのWebサーバの方式には以下の3つがあります。
- マルチプロセスモデル(Apache)
- マルチスレッドモデル(Apache)
- イベント駆動型モデル (Apche・Nginx)
1.2の説明については以下の資料に記載されています。
マルチ◯◯モデルは、Apacheがずっと使ってきたモデルですがC10K問題と呼ばれる、サーバへのクライアントアクセス数に起因する問題のため、問題視されています。
TheC10kProblem - 「C10K問題」(クライアント1万台問題)とは、ハードウェアの性能上は問題がなくても、あまりにもクライアントの数が多くなるとサーバがパンクする問題のこと
そこでNginxではイベント駆動型モデルを採用しています。
イベント駆動型モデルとは、クライアントのアクセスをイベントとして扱い、それをきっかけにプロセス内で処理を開始することです。
その他のモデルとの大きな違いは「1プロセス1スレッド」であるということです。そのためクライアント数が増加してもプロセス数は増えないため、Apacheのモデルと比較してメモリを削減できます。
I/Oの多重化について
さて、イベント駆動式についてはわかりました。では次に1プロセスでどうやって、多数のクライアントからのアクセスをさばいているのかを確認します。
Webサーバーのように同時に複数の処理を行うには、ディスクやネットワークといったI/Oの多重化が必要になります。I/O多重化の実装には「select」や「poll」といったシステムコールが従来使われています。
同期IOを使用した場合、1台のクライアントからのアクセスだけでプロセスが専有されてしまうため、複数のクライアントへのレスポンスを返すことができません。
これを回避するために以下の方法があります。
- マルチプロセス(Apache)
- マルチスレッド(Apahce)
- 非同期I/O
- I/Oの多重化(Apache、Niginx)
I/Oの同期、非同期についてはこちらを参照
そして、このI/Oの多重化を実現するためのシステムコール「select, poll, epoll, kqueue」があるそうです。
システムコール「select, poll, epoll」について
プログラムで複数のファイルディスクリプタを監視し、 一つ以上のファイルディスクリプタがある種の I/O 操作の 「ready (準備ができた)」状態 (例えば、読み込み可能になった状態) になるまで待つことができる。
詳しくはこちらのサイトで解説されています。
select, poll, epollにはそれぞれ特徴がありますが、使用して早いのはLinux2.6から採用された「epoll」です。
select、pollの場合
プロセスがselectを発行する際に、ファイルディスクリプタセットと呼ばれる複数のファイルディスクリプタをまとめたリストをカーネルに渡します。
カーネルはリストを読み込み、それぞれのファイルディスクリプタの状態を確認しリストを更新します。(リストに含まれているディスクリプタの数だけループする)その後、処理が終わった後カーネルがリストを返却し、状態がreadyになったファイルディスクリプタの処理をプロセスが進めます。
この処理だと、ファイルディスクリプタの数だけループが発生するため処理に時間がかかります。
epollの場合
epollでは指定のファイルディスクリプタの状態をカーネルに問い合わせるだけです。そのため高速な処理が可能となります。
※カーネルがディスクリプタの状態を管理している
まとめ
Nginxでは、多数のクライアントからの接続をきっかけに仕事が始まります。その仕事はI/Oが発生すると、別の処理に飛び新しい仕事をはじめます。
I/Oの管理は、epollによって行われI/Oが終了した処理はクライアントへ返却されます。