コンテンツにスキップ

ブロックせずに待つ

nginxのワーカーは、1つのイベントループで多くの接続を同時に処理します。sleepやソケットの読み込みのような、ブロックする呼び出しでハンドラが待つと、ワーカー全体が止まります。Nginx::Async.sleepとNginx::Async::HTTP.sub_requestを使うと、ハンドラはnginxを通して待ちます。ハンドラのRubyのコードはファイバーの中で動きます。このファイバーはnginxに制御を返し、nginxがイベントループからそのファイバーを再開します。

accessハンドラがNginx::Async.sleepを呼んでから、ハンドラのコードが先へ進むまでに起きることを示します。ngx_mrubyのソースの次の行から描きました。2026-10-19のコミット0c72e3970の行です。
  • mrbgems/ngx_mruby_mrblib/mrblib/mrb_nginx.rbの100行から106行
  • src/http/ngx_http_mruby_async.cの187行から239行、241行から314行、316行から348行、363行から410行
  • src/http/ngx_http_mruby_module.cの1061行から1074行
  • src/http/ngx_http_mruby_core.hの30行から35行
  • ngx_mrubyは、ngx_mrb_start_fiberで、各ハンドラのコードをmrubyのファイバーの中で実行します。Nginx::Async.sleepは、nginxのタイマーをセットしてファイバーを中断します。ファイバーが中断している間、ハンドラは既にNGX_DONEを返していて、ワーカーはほかの接続を処理します。
  • タイマーが切れると、ngx_mrb_timer_handlerが、ファイバーを、それが属するリクエストとともに再開します。コードが終わると、ngx_mrubyはハンドラの結果をnginxに渡します。結果がNginx::DECLINEDならそのフェーズの次のハンドラが動き、ほかの結果ならnginxはリクエストを終えます。
  • Nginx::Async::HTTP.sub_requestも同じように動きます。このメソッドはnginxのサブリクエストを始めてファイバーを中断し、サブリクエストが終わるとnginxがファイバーを再開します。
  • mruby_setとpost-read、server rewrite、rewrite、accessのハンドラは待機して再開できます。中断したハンドラはNGX_DONEを返し、サブリクエストの処理中はNGX_AGAINを返します。
  • 2.7.3の修正以降、通常のcontentハンドラもNginx::Async.sleepで待てます。この修正は3.xにも統合されています。nginxがリクエストを保持し、タイマーでファイバーを再開します。その応答や戻り値のステータスでリクエストが完了します。リリース済みの2.7.2までは、sleepの時点で応答せずに終了していました。
  • RubyのNginx.redirectから呼ばれたcontentハンドラには、sleep時点で終了する制限が残ります。呼び出し元も処理を続け、その場で応答する場合があります。
  • contentハンドラでNginx::Async.sleepの待機後にNginx::Async::HTTP.sub_requestを呼ぶと、例外はRuntimeError。
  • mruby_setのサブリクエストはリクエストを保持しません。変数の完了を待たず、nginxはcontentフェーズへ進みます。
  • ヘッダフィルタ、ボディフィルタ、logハンドラでは、両メソッドがRuntimeErrorを起こします。
  • mruby-sleepのsleep、mruby-socketのソケット、ファイル、シェルのコマンド、Redisの呼び出しで待つコードは、その間ワーカーをブロックします。詳しくは信頼境界にあります。

リファレンスのNginx::Asyncに、両方のメソッドの説明があります。

関連する制約の資料: