Slide 1

Slide 1 text

ngx_mrubyとfiberの話 (未完成版) @syu_cream

Slide 2

Slide 2 text

whoami ● @syu_cream ● 仕事で Go, Scala, mrubyは趣味 ● mruby の実装に関する同人誌書いた ○ https://www.amazon.co.jp/dp/B06XZ9LXSB ● 作った mruby 関連プロダクト ○ mruby-rspec ○ mruby-serverspec ○ mruby-k2hash ○ mruby-rocksdb ○ ts_mruby(mruby extension for Apache TrafficServer)

Slide 3

Slide 3 text

今日の話 ● この会のテーマを見ると... ● ngx_mruby, fiber 周りで試行錯誤した話需要あるかも? ● と思ったのでそんな感じの話します

Slide 4

Slide 4 text

ngx_mruby ● 設定や簡単なロジックを mruby で拡張可能にする nginx モ ジュール ○ https://github.com/matsumotory/ngx_mruby ● ngx_mruby 利用例: ○ ランダムに upstream を切り替える ○ 特定 IP アドレス以外からのアクセスを遮断する

Slide 5

Slide 5 text

ngx_mruby 利用の具体例 ● User-Agent が空なリクエストに400を返す ○ 以下はnginx.confにインラインで書いてるけど別ファイルに分離可能

Slide 6

Slide 6 text

ngx_mruby における非同期処理 ● 依存 mrbgems で容易に I/O でブロックする ○ ノンブロッキング I/O のサポートはしていない ○ なので性能面でつらい場面が出る可能性がある ● h2oでも試行錯誤してた ○ https://www.slideshare.net/ichitonagata/h2o-x-mruby-72949986 ● ngx_lua はノンブロッキング処理を頑張っている ○ ngx.sleep, ngx.location.capture ○ ngx.socket.tcp, ngx.socket.udp ○ さらに気になる人は “nginx実践入門” もチェック!

Slide 7

Slide 7 text

ngx_mrubyでもノンブロッキングI/Oしたい! ● nginx を使う以上イベント駆動の恩恵を受けたい ● lua ならコルーチンで処理の継続ができる ● mruby の場合... 継続可能な処理は fiber で表現可能 ● まずノンブロッキング sleep を実装してみる ○ 実用性で言うと subrequest が欲しいが難易度が高い ○ 第一歩は簡単で分かりやすい目標にする

Slide 8

Slide 8 text

ngx_mruby fiber ● ngx_mruby で fiber サポートを試みた ○ まずは mruby の実行コードを fiber に包んで VM に実行させる ○ その後 fiber の具体利用例として sleep を実装する ■ Nginx::Async.sleep という名前付けた

Slide 9

Slide 9 text

事前調査・学習の流れ ● mruby の ソースコードをざっくり読む ○ include/ 以下のヘッダと必要なら src/, mrbgems/mruby-fiber/ ● ngx_mruby のソースコードを読む ○ 特に src/http/ngx_http_mruby_module.c ○ VM の実行部分を触ることになるので mrb_run() 周りを ● echo-nginx-module のソースコードを読む ○ ノンブロッキング sleep を実装している module ■ えっ、 echo module って何だよ(哲学) ■ src/ngx_http_echo_sleep.c が参考になった ● nginx のソースコードをざっくり読む ○ 上記で解決しなかった場合はサボらない

Slide 10

Slide 10 text

実装 ● forkした以下のリポジトリにあります ○ https://github.com/syucream/ngx_mruby ● やったこと ○ mrb_run() で実行する RProc 構造体の値を fiber でラップ ○ fiber を yield されるまで実行 ○ Nginx::Async.sleepのC関数内で yield して nginx の timer をセット ■ ngx_add_timer() にハンドラと秒数を渡す ○ ハンドラ内で fiber を resume & nginx のイベントを進める ■ ngx_http_core_run_phase() 経由で操作

Slide 11

Slide 11 text

Nginx::Async.sleep の動作 ● コード例 ○ 上がブロックする版、下がしない版 ● 実際の動作 ○ デモします

Slide 12

Slide 12 text

ngx_mruby によるノンブロッキング処理の展望 ● sleep だけでは用途は限定される ● subrequest が扱えるとやれることが多くなる ○ ACL 設定を他 HTTP サーバに分離してそれを参照など ● ngx_lua の ngx.socket.tcp など欲しいが、険しい道 ○ mruby-redis とか使いたいライブラリに手を入れるの? ○ 片っ端から ngx_mruby 用の修正入れるのは高コスト ● Thread Pool と連携してなにかできないかな? ○ https://www.nginx.com/blog/thread-pools-boost-performance-9x/ ○ ブロックする処理の実装は無理して変更しない ○ Thread Pool に投げるようにして、終わったら resume する

Slide 13

Slide 13 text

実装でハマった(ハマってる)箇所1 ● mruby の fiber 操作の C 関数がつらい ○ yield/resume 関数の 2 つしかない ○ new する C 関数が無いので funcall 経由でオブジェクト生成 ■ proc オブジェクトと fiber class の mrb_value を用意 ■ mrb_funcall_with_block() に渡してオブジェクトを得る ○ その後 mrb_fiber_resume() するも... ● 本スライドでは Fiber を Proc オブジェクトでラップして Proc#call 経由で操作するメソッドを Ruby で実装☺

Slide 14

Slide 14 text

実装でハマった(ハマってる)箇所2 ● 生成された mruby のコードの OP_STOP 命令問題 ○ yield した後もちろん別の mruby コードが実行される可能性ある ○ そしてそのコードは末尾に OP_STOP を持つ ■ mrb_generate_code() で生成されたコードは持つはず ○ OP_STOP で VM が停止し、 fiber が resume できない事態に! ○ 一旦 OP_STOP を OP_RETURN に置換するという荒業で対応 ■ 過去に h2o がこの対策取ってたらしい ■ 本来どうするべきなんだろうか ...

Slide 15

Slide 15 text

実装でハマった(ハマってる)箇所3 ● 特定ハンドラでしか動作確認できていない ○ 現状、 rewrite, access handler でしか動作しない ○ content handlerとかで動かしたら死ぬ ● nginx の理解がそもそも足りていない気がする

Slide 16

Slide 16 text

おわりに ● ngx_mrubyの試行錯誤コード読み書き話ウケるかも! ● と思ったのでそんな感じの話します ● と意気込んだものの絶賛WIPです ● それでも何かの参考になれば幸いです