Successfully reported this slideshow.

アプリケーションへのRubyインタープリターの組み込み

2

Share

Upcoming SlideShare
SCM, CI and Maven Repo
SCM, CI and Maven Repo
Loading in …3
×
1 of 55
1 of 55

More Related Content

Related Audiobooks

Free with a 14 day trial from Scribd

See all

アプリケーションへのRubyインタープリターの組み込み

  1. 1. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Embed Ruby アプリケーションへの Rubyインタープリターの組み込み 須藤功平 株式会社クリアコード 東京Ruby会議11 2016-05-28
  2. 2. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Speaker's award Continuous development award
  3. 3. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 受賞者 cedlemo Continuous development award
  4. 4. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 受賞理由 2015年1月から継続的に Ruby-GNOME2の開発に 参加しているから 一発すごい改善をした人よりも 地味でも継続的に改善している人を評価したい Continuous development award cedlemo
  5. 5. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 宣伝 OSS Gate
  6. 6. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 OSS Gate OSS開発に 参加する人を 増やす取り組み
  7. 7. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 背景 OSS利用は当たり前になった →OSS利用者増加✓ ✓ 開発参加者も増えるといいな →OSS増加✓ ✓ OSS Gate
  8. 8. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 OSS開発参加 すごい改善じゃなくていい✓ バグレポートとかでいい typo見つけました!とかでもいい✓ サンプルを更新とかでもいい✓ ✓ OSS Gate
  9. 9. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 OSS Gate参加の動機 人それぞれでいい OSS Gate
  10. 10. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 私の動機 ユーザーが自由に使える ソフトウェアが増えるといいな ✓ 自由に使える例: コードを読んで学習できる✓ 今日聞いた話の実装を確認できる!✓ ✓ OSS Gate
  11. 11. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 興味ある? 興味?(重要:動機不問) OSS開発に参加したい!✓ OSS開発参加者を増やしたい!✓ ✓ 5階でワークショップ開催中 説明や見学は私に一声かけて✓ ✓ OSS Gate
  12. 12. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 本題 Rubyの組み込み (CアプリケーションへのRubyインタープリターの組み込み)
  13. 13. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 動機 柔軟な記述力が欲しい✓ Cの速さが欲しい✓ Ruby以外の言語とも連携したい Pythonも組み込む✓ ✓ なんかカッコいい✓ Rubyの組み込み
  14. 14. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 別の実現方法 拡張ライブラリー Rubyの組み込み
  15. 15. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 組み込みと拡張ライブラリー C Ruby Ruby C Ruby組み込み 拡張 ライブラリー Rubyの組み込み
  16. 16. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 拡張ライブラリー 実現可能: 柔軟な記述力が欲しい✓ Cの速さが欲しい✓ ✓ 実現不可能: Ruby以外の言語とも連携したい✓ なんかカッコいい感✓ ✓ Rubyの組み込み
  17. 17. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 実現方法の選び方 基本は拡張ライブラリー✓ すでにあるアプリなら組み込み✓ 選びたい方があるならそっち✓ Rubyの組み込み
  18. 18. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 組み込みを選ぶ時の注意 それなりの覚悟が必要 利用例があまりない 問題遭遇確率が高い✓ ✓ 問題遭遇時: 自分でソースを読んで調べる✓ 詳しい人に相談 ささださん<できればサポートを強化したい ✓ ✓ Rubyの組み込み
  19. 19. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 組み込み方を紹介 実例 milter manager Since 2008
  20. 20. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 milter manager メール サーバー milter manager メール フィルター メール フィルター メール フィルターmilter Ruby milterを管理するmilter サーバープロセス
  21. 21. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みの実装 初期化✓ fork対応✓ イベントループとシグナル✓ milter managerへのRubyの組み込み
  22. 22. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:GC関連 { /* スタックの底を設定 */ /* GC時にCのローカル変数に代入されている Rubyのオブジェクトをマークするため */ RUBY_INIT_STACK; /* ... */ } milter managerへのRubyの組み込み
  23. 23. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 スタックとマーク対象 { RUBY_INIT_STACK; /* ... */ { VALUE object = rb_ary_new(); /* マーク対象 */ } } { VALUE object = rb_ary_new(); /* マーク対象外 */ } milter managerへのRubyの組み込み
  24. 24. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 確認例 #define MARKED_P(object) rb_objspace_marked_object_p(object) { RUBY_INIT_STACK; { VALUE object = rb_ary_new(); /* GC.start(immediate_sweep: false) */ printf("%dn", MARKED_P(object)); /* => 1 */ } } { VALUE object = rb_ary_new(); /* GC.start(immediate_sweep: false) */ printf("%dn", MARKED_P(object)); /* => 0 */ } スライドのリポジトリー:examples/gc.c
  25. 25. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 GC関連の注意 { RUBY_INIT_STACK; /* Rubyのオブジェクトを触る Cのコードはこのブロック内でだけ使うこと */ /* Cからのコールバックで Rubyのコードを呼び出すときは注意 */ } milter managerへのRubyの組み込み
  26. 26. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:シグナル関連 { RUBY_INIT_STACK; /* シグナルハンドラーを保存 */ ruby_init(); /* Rubyがシグナルハンドラーを登録 */ /* シグナルハンドラーを復帰 */ /* シグナルはアプリで処理したいから */ } milter managerへのRubyの組み込み
  27. 27. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 シグナル復帰例 { /* 他のシグナルも同様に復帰 */ void (*sigint_handler)(int); sigint_handler = signal(SIGINT, SIG_DFL); ruby_init(); signal(SIGINT, sigint_handler); } milter managerへのRubyの組み込み
  28. 28. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:引数の処理 { /* ...ruby_init()... */ static char *argv_raw[] = {"milter-manager", "-e;"}; int argc; char **argv; argc = sizeof(argv_raw) / sizeof(char *); argv = argv_raw; ruby_incpush(/* ... */); /* $LOAD_PATHの設定 */ /* 中でいろいろ初期化するのでダミーの引数で呼ぶ */ ruby_process_options(argc, argv); } milter managerへのRubyの組み込み
  29. 29. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 初期化:アプリの初期化 { /* ...ruby_process_options()... */ /* require中に例外が発生してもここで止める */ /* ここで止めないと例外を受け取る人がいなくて クラッシュ */ rb_protect(/* rb_require("milter/manager") */); } milter managerへのRubyの組み込み
  30. 30. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 milter managerとRuby 組み込み処理系の1つ Pythonも使えるようにしたかった 結局Ruby必須でPython対応はしなかった ✓ ✓ 起動後にdlopen()で動的にsoを 読み込んで組み込み ✓ milter managerへのRubyの組み込み
  31. 31. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 起動時に動的に組み込み milter manager Ruby milter manager Ruby 起動前 起動 起動後 milter manager Ruby dlopen() milter managerへのRubyの組み込み
  32. 32. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 起動時にso読んで組み込み { /* ↓GC用 */ RUBY_INIT_STACK; /* 動的にsoを読んで初期化関数を呼ぶ */ /* dlopen(); init = dlsym(); init(); ←の中でruby_init();とか */ /* アプリの処理 */ } milter managerへのRubyの組み込み
  33. 33. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 RUBY_INIT_STACK!? { /* ↓アプリ側で呼ぶの!? */ RUBY_INIT_STACK; /* 動的にsoを読んで初期化関数を呼ぶ */ /* dlopen(); init = dlsym(); init(); ←の中でruby_init();とか */ /* アプリの処理 */ } milter managerへのRubyの組み込み
  34. 34. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 アプリにーlruby… milter manager Ruby milter manager Ruby 起動前 起動 起動後 milter manager Ruby dlopen() -lruby カッコわるい。。。 milter managerへのRubyの組み込み
  35. 35. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込み時の意気込み 本体に組み込む✓ 動的に組み込もうとしない✓
  36. 36. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みの実装 初期化✓ fork対応✓ イベントループとシグナル✓ milter managerへのRubyの組み込み
  37. 37. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 milter manager利用例 大学・企業 ユーザー数:数百〜数万人✓ ✓ プロバイダー ユーザー数:数千〜数十万人✓ ✓ それなりの性能が必要 milter managerへのRubyの組み込み
  38. 38. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 性能向上方法 CPU マルチプロセス1択✓ ✓ 通信・多同時接続 いろいろ✓ ✓ milter managerへのRubyの組み込み
  39. 39. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 マルチプロセス マスタープロセス listen()a. fork()b. ✓ ワーカープロセス accept()a. ↑したクライアントの処理b. ✓ milter managerへのRubyの組み込み
  40. 40. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みとfork() fork()すると ワーカープロセスがクラッシュ プロセス終了時とか✓ ✓ ヒント:fork()とスレッド✓ milter managerへのRubyの組み込み
  41. 41. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 fork()とスレッド 混ぜるな危険✓ Rubyはスレッドを動かしている 例:タイマースレッド✓ ✓ fork時にスレッドのケアが必要✓ milter managerへのRubyの組み込み
  42. 42. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 スレッドのケア VALUE rb_pid; /* タイマースレッドの後始末とか した上でfork */ rb_pid = rb_funcall(rb_mKernel, rb_intern("fork"), 0); return NUM2INT(rb_pid); milter managerへのRubyの組み込み
  43. 43. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Ruby組み込みの実装 初期化✓ fork対応✓ イベントループとシグナル✓ milter managerへのRubyの組み込み
  44. 44. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 イベントループとシグナル 気にしなくてよい アプリがシグナルを処理するから✓ ✓ 拡張ライブラリーなら対応必要 イベントループ中にシグナル発生✓ →すぐにイベントループを抜ける✓ ✓ milter managerへのRubyの組み込み
  45. 45. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Rubyの組み込みのまとめ Rubyを組み込む実装方法を紹介✓ 動的組み込みは諦めろ✓ fork時はRubyのforkを使う✓ Rubyの組み込み
  46. 46. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 mrubyの組み込み 実例 Groonga Since 2013
  47. 47. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 Groongaとmruby Groonga 全文検索エンジン(mruby組み込み)✓ 高速に検索結果を返し続けたい✓ リソース消費は波がない方がよい 例:いらなくなったメモリーはすぐに解放 ✓ ✓ mrubyの組み込み
  48. 48. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 リソース消費 時間 メモリー使用量 こっちが望ましい mrubyの組み込み
  49. 49. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 メモリー管理 Groonga 必要なときに確保✓ いらなくなったら解放✓ ✓ mruby GC✓ メモリーが足りなくなったら解放✓ ✓ mrubyの組み込み
  50. 50. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 GroongaとmrubyのGC mrubyのGCにGroongaのリソース 管理を任せない リソース管理: mrubyのオブジェクトのsweep時に Groongaのリソースを解放 ✓ ✓ mrubyの組み込み
  51. 51. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 mrubyのGCとリソース mrubyのGC Groongaリソースのサイズを知らない✓ 適切なタイミングでsweepできない✓ RubyのGCも同じ✓ ✓ mrubyの組み込み
  52. 52. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 実例1: 明示的な解放 # 検索 result = table.search(condition) begin output_result(result) # 出力 ensure result.close # 明示的な解放 end mrubyの組み込み
  53. 53. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 実例2: 所有権を渡さない /* Groonga側でリソース確保 */ expr = grn_expr_create(/* ... */); /* mrubyのオブジェクトとしてラップ */ mrb_expr = grn_mrb_value_from_grn_obj(mrb, expr); /* mruby側は参照して処理する */ mrb_size = mrb_funcall(mrb, mrb_expr, "estimate_size", 1, mrb_table); /* Groonga側でリソース解放 */ grn_expr_close(expr); mrubyの組み込み
  54. 54. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 mruby組み込みのまとめ GCに任せないという選択 使用メモリー量を安定させるため✓ 安定した性能を出すため✓ ✓ mrubyの組み込み
  55. 55. Embed Ruby - アプリケーションへのRubyインタープリターの組み込み Powered by Rabbit 2.1.9 まとめ Rubyの組み込み ガッツリ連携するつもりで設計 拡張ライブラリーで十分じゃないかよく検討すること ✓ ✓ mrubyの組み込み アプリの大事な事を忘れないで設計✓ ✓ OSS Gateもよろしく✓

×