PHP-FPM のリクエスト処理後のタイムアウトの仕組みのまとめ
少し前に PHP の FPM (FastCGI Process Manager) のリクエスト処理後のタイムアウトの仕組みについて調べたのでかんたんにまとめておきます。
fastcgi_finish_request()
FPM で PHP を動かすと、 fastcgi_finish_request()
という関数が利用できます。
この関数はリクエスト処理を完了するためのもので、 PHP の公式サイトによると次のような処理を行うそうです。
PHP: fastcgi_finish_request - Manual
This function flushes all response data to the client and finishes the request. This allows for time consuming tasks to be performed without leaving the connection to the client open.
意訳:
この関数(
fastcgi_finish_request()
)は、レスポンスデータをすべてクライアントに送ってリクエストを終了します。 この関数を使うことで、クライアントとのコネクションを閉じた後に時間のかかるタスクを実行できます。
少し表現の異なる別の説明もあるのでそちらも引用します。
PHP: FastCGI Process Manager (FPM) - Manual
fastcgi_finish_request()
- special function to finish request and flush all data while continuing to do something time-consuming (video converting, stats processing etc.);
意訳:
fastcgi_finish_request()
- リクエストを終了させてすべてのデータをフラッシュします。 フラッシュ後も処理を継続し時間のかかるタスク(動画の加工や統計処理など)を実行できます。
つまり、 fastcgi_finish_request()
を使えば、時間のかかるタスクを(サーバーとは別の cron やタスクワーカーではなく)通常のリクエストハンドリングのプロセスの中で処理することができます。
cron やタスクワーカーが使いづらい状況や使うまでもない状況ではとても便利な機能です。
fastcgi_finish_request()
呼び出し後のタイムアウト設定
FPM のデフォルトの設定では、 fastcgi_finish_request()
の呼び出し後はタイムアウトがかかりません。
どれだけ時間がかかっても中断されないので、万が一のケースとして処理がいつまで経っても終わらないなんてことが起こらないようにアプリケーションのレイヤーで適切に処理する必要があります。
なお、 fastcgi_finish_request()
の呼び出し後は PHP の設定値 max_execution_time
も効かないので注意が必要です。
このタイムアウト設定は request_terminate_timeout_track_finished
で変更できます。
デフォルトでは no
になっていますが、これを yes
に変えることで fastcgi_finish_request()
呼び出し後の処理にタイムアウト時間を設定することができます。
そのときの時間を設定するための項目は request_terminate_timeout
です。
; request_terminate_timeout() 呼び出し後のタイムアウトを設定する
request_terminate_timeout_track_finished = yes
request_terminate_timeout = 300s
このあたりの情報については、(記事執筆時点では) PHP のドキュメントには詳しい説明が無かったので、私は php/php-src
のリポジトリの中のインラインコメントを参考にしました。
; The timeout for serving a single request after which the worker process will
; be killed. This option should be used when the 'max_execution_time' ini option
; does not stop script execution for some reason. A value of '0' means 'off'.
; Available units: s(econds)(default), m(inutes), h(ours), or d(ays)
; Default Value: 0
;request_terminate_timeout = 0
; The timeout set by 'request_terminate_timeout' ini option is not engaged after
; application calls 'fastcgi_finish_request' or when application has finished and
; shutdown functions are being called (registered via register_shutdown_function).
; This option will enable timeout limit to be applied unconditionally
; even in such cases.
; Default Value: no
;request_terminate_timeout_track_finished = no
リクエスト処理完了後に時間がかかるタスクを実行できる仕組みは、たとえば FaaS を使って PHP アプリケーションを動かすときなどに有用で使いどころはそれなりにあるのかなと個人的には思いますが、あまり参考になる情報が見つかりませんでした。 同じ問題にあたった方のご参考になれば幸いです。