ブロックせずに待つ
nginxのワーカーは、1つのイベントループで多くの接続を同時に処理します。sleepやソケットの読み込みのような、ブロックする呼び出しでハンドラが待つと、ワーカー全体が止まります。Nginx::Async.sleepとNginx::Async::HTTP.sub_requestを使うと、ハンドラはnginxを通して待ちます。ハンドラのRubyのコードはファイバーの中で動きます。このファイバーはnginxに制御を返し、nginxがイベントループからそのファイバーを再開します。
accessハンドラのコードが、ファイバーの中で呼ぶ
Nginx::Async.sleep(500)を__sleepがFiber.yieldがハンドラは返す。nginxはリクエストを保ったまま、ハンドラを離れる
NGX_DONEをタイマーが動いている間、ワーカーはほかの接続を処理する
タイマーが切れると、ファイバーを再開する
ngx_mrb_timer_handlerがコードは後から続く。コードが終わると、リクエストは次のハンドラへ進むか、nginxがハンドラの結果でリクエストを終える
sleepの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::
関連する制約の資料: