2014年10月27日月曜日

リクエスト処理の流れ: listener_thread(5) タイムアウトキューの処理

(14.4) イベント処理のつづき。

(14.5) タイムアウトキューの処理


次は、4つ用意されているタイムアウトキューに対する、タイムアウト処理だ。
タイムアウトに至る前にイベントが発生して、1655行目までのソケットイベント処理で消化されれば
特に何も行われない。
タイムアウトに至るまでに、ソケットの読み込み/書き出しが可能とならなかった場合に、それらソケットが次の処理の対象となる。

   1656
   1657         /* XXX possible optimization: stash the current time for use as
   1658          * r->request_time for new requests
   1659          */
   1660         now = apr_time_now();

まず、現在時刻を取得する。単位はマイクロ秒だ。

   1661         /* we only do this once per 0.1s (TIMEOUT_FUDGE_FACTOR) */
   1662         if (now > timeout_time) {

timeout_timeは初期値は0なので、最初はこのif文に必ず入る。
そして、timeout_timeに現在時刻のTIMEOUT_FUDGE_FACTOR後にセットしている(1664行目)。
定義を見ると、100000マイクロ秒なので、0.1秒。

   1396 #define TIMEOUT_FUDGE_FACTOR 100000

おおむね、0.1秒以上間隔をあけてチェックするということだろう。

   1663             struct process_score *ps;
   1664             timeout_time = now + TIMEOUT_FUDGE_FACTOR;
   1665
   1666             /* handle timed out sockets */
   1667             apr_thread_mutex_lock(timeout_mutex);
   1668
   1669             /* Step 1: keepalive timeouts */
   1670             /* If all workers are busy, we kill older keep-alive connections so that they
   1671              * may connect to another process.
   1672              */

(1)KeepAliveタイムアウト

keepalive_qキューを処理する。

   1673             if (workers_were_busy && keepalive_q.count) {

この時点でworkers_were_busyが1の場合(アイドルworkerスレッドがいない場合)で、
KeepAliveタイムアウトキューに登録がある場合(次のリクエストを待っている場合)、
現在時刻(+0.1秒)からKeepAliveTimeOut先までのタイムアウトイベントを先取りして
リンガリングクローズを実行している。
上記のコメントによれば、すべてのworkerがビジーなら、古いKeep-Alive接続を終了し、
クライアントが必要になった時点で別のプロセスに接続できるようにするということだ。

しかし、KeepAliveTimeOut先でこのkeepalive_qキューのタイムアウトは登録されるのだから、
古いというか、おそらくこの時点で登録済みのすべてのKeepAlive待ちイベントが
全てstart_lingering_close_nonblocking()関数処理されると考えられる。

start_lingering_close_nonblocking()関数は、
既に見た
start_lingering_close_blocking
と異なり、shutdown(SHUT_WR)の前に未出力のデータのフラッシュを試みない。
それ以外は同様で、この処理によって、ソケットはLINGER待ちになりlinger_qキュー、またはshort_linger_qキューに
登録される。

   1674                 ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
   1675                              "All workers are busy, will close %d keep-alive "
   1676                              "connections",
   1677                              keepalive_q.count);
   1678                 process_timeout_queue(&keepalive_q,
   1679                                       timeout_time + ap_server_conf->keep_alive_timeout,
   1680                                       start_lingering_close_nonblocking);
   1681             }
   1682             else {

こちらは、通常の場合で、
timeout_time(現在時刻+0.1秒)時点でタイムアウトしているタイムアウトイベントを
同じく、start_lingering_close_nonblocking()関数処理している。


   1683                 process_timeout_queue(&keepalive_q, timeout_time,
   1684                                       start_lingering_close_nonblocking);
   1685             }
   1686             /* Step 2: write completion timeouts */

(2)書き込み完了タイムアウト


ここは、write_completion_qキューを処理する。
タイムアウト時間が来ていた場合に行う処理は、start_lingering_close_nonblocking()で、
これはKeepAlive待ちと同じだ。
通常は、shutdown(SHUT_WR)が行われ、linger_qキューに登録される。

   1687             process_timeout_queue(&write_completion_q, timeout_time,
   1688                                   start_lingering_close_nonblocking);
   1689             /* Step 3: (normal) lingering close completion timeouts */

(3)リンガリングクローズタイムアウト(標準)

linger_qキューの処理だ。
ここでのタイムアウト時の処理は、stop_lingering_close()だ。
この処理は、ソケットをクローズする。

   1690             process_timeout_queue(&linger_q, timeout_time, stop_lingering_close);
   1691             /* Step 4: (short) lingering close completion timeouts */

(4)リンガリングクローズタイムアウト(短時間)

short_linger_qキューの処理だ。
ここでのタイムアウト時の処理も、linger_qキューと同じで、stop_lingering_close()だ。
ソケットをクローズする。

   1692             process_timeout_queue(&short_linger_q, timeout_time, stop_lingering_close);
   1693

ここまででタイマーキューの処理は終わりになる。

以下は、いくつかの管理情報の更新処理だ。

   1694             ps = ap_get_scoreboard_process(process_slot);
   1695             ps->write_completion = write_completion_q.count;
   1696             ps->keep_alive = keepalive_q.count;
   1697             apr_thread_mutex_unlock(timeout_mutex);
   1698
   1699             ps->connections = apr_atomic_read32(&connection_count);
   1700             ps->suspended = apr_atomic_read32(&suspended_count);
   1701             ps->lingering_close = apr_atomic_read32(&lingering_count);
   1702         }
   1703         if (listeners_disabled && !workers_were_busy
   1704             && (int)apr_atomic_read32(&connection_count)
   1705                - (int)apr_atomic_read32(&lingering_count)
   1706                < ((int)ap_queue_info_get_idlers(worker_queue_info) - 1)
   1707                  * worker_factor / WORKER_FACTOR_SCALE + threads_per_child)
   1708         {
   1709             listeners_disabled = 0;
   1710             enable_listensocks(process_slot);
   1711         }
   1712         /*
   1713          * XXX: do we need to set some timeout that re-enables the listensocks
   1714          * XXX: in case no other event occurs?
   1715          */
   1716     }     /* listener main loop */

ここまでで主ループは終わる。

listenerスレッドの終了処理では、Listenソケットをクローズし、workerスレッド向けのメッセージキューに終了通知を登録する。

   1717
   1718     close_listeners(process_slot, &closed);
   1719     ap_queue_term(worker_queue);
   1720
   1721     apr_thread_exit(thd, APR_SUCCESS);
   1722     return NULL;
   1723 }

これで、listener_threadの処理を終わる。

0 件のコメント:

コメントを投稿