そこに、event.cファイルがある。
このファイルには、MPMの主処理(mpmフック関数)である event_run()関数とそこから起動される子プロセスのコードが定義されている。
どういった処理構成なのか、図を描くと分かりやすいと思ったので、描いてみた。
親プロセスで event_run()関数が実行され、子プロセスを生成する。
通常は、StartServersディレクティブで指定された数の子プロセスが生成される。
各子プロセスの主処理は child_main()関数である。
このchild_main()関数の処理スレッドをメインスレッドと書いた。メインスレッドは各スレッドを生成するためのstartスレッドを開始し、その後、親プロセスからの終了コマンドを受け付けるためにpipeを監視している。
startスレッドは、設定のThreadsPerChildで定められた数のworkerスレッドを生成し、1つのlistenerスレッドを生成すると終了する。workerスレッドの生成は、graceful再起動の場合など時間がかかるケースがあるが、startスレッドの寿命は大体短い。
ThreadPerChild個のworkerスレッドは、StartServers個のプロセス全部で用意されるので、最初のworkerスレッド数はその掛け算 ( ThreadPerChild × StartServers )個になる。
listenerスレッドは、設定ファイルのListenディレクティブで指定されたサーバ用のソケットの監視が当初の役目だ。
ソケットが接続を受け付けると、通信用のソケットが払い出され、これをworkerスレッドに引渡す。
workerスレッドはリクエスト処理のためのスレッドだ。
通信中のソケットを受け取り、HTTPリクエストを処理する。
なお、Listenソケットは、親プロセスで生成される。
実際に接続を受け付けるaccept()の処理を行うのは子プロセスのlistenerスレッドだが、親プロセスで用意されたソケットリソースが全ての子プロセスで共有される構成になっている。
さて、event MPMの場合、workerスレッドが処理したリクエストに対してレスポンスを返した後、そのソケットがHTTPのKeep-Aliveが有効な通信の場合、次のリクエスト待ちとして、そのソケットをlistenerスレッドの監視対象に加える。それを図では接続済みソケット、と書いた。
そして、処理を終えたworkerスレッドはアイドル状態のworkerスレッドとなって、次のリクエストの到着を待つ。
ちなみに、worker MPMの場合は、Keep-Aliveな通信でworkerスレッドがレスポンスを返した後、引き続き、そのソケットに次のリクエストが到着するのをそのまま待つようになっている。
待ってる間はworkerスレッドは何も処理をしない。もし、listenerスレッドが他に新しいリクエストを受け取っても、そのworkerスレッドでは処理できない。
この次のリクエストの待ち時間が無駄だと考えられたようだ。
0 件のコメント:
コメントを投稿