トップ «前の日記(2014/11/12 (水) ) 最新 次の日記(2014/11/14 (金) )» 編集 RSS feed

HsbtDiary


2014/11/13 (木) [長年日記]

[mruby][ruby] 30d.jp に ngx_mruby を導入した話

(社内で共有した内容の転記です)

30d.jp に ngx_mruby を導入して、誰も手出しができないミドルウェアの置き換えとパフォーマンス向上を実現しました。

前提要件

  • 30d.jp に保存している画像はサービスにログインしている時のみ見えるようにしたい
  • 画像は MogileFS に保存しており、Rails を介さないでレスポンスを返したい

ngx_mruby 導入前のシステム構成

3a6051dc-658c-11e4-9ce1-d4574aa8b7ef

ngx_mruby 導入後のシステム構成

3a611d06-658c-11e4-8243-eb960a80ffe7

導入後の効果

siege を用いたベンチマーク(siege -b -c 8 -r 50)でレスポンスタイムで 1割ちょっと、転送レートで 2割ちょっとの改善となりました。

Elap Time,  Resp Time,  Trans Rate,  Throughput,  Concurrent
**** nginx-1.6.2 + mruby ****
14.98,       0.26,       26.70,        4.54,        6.85
**** nginx-1.6.2 + perlbal ****
18.26,       0.29,       21.91,        3.72,        6.27

さらに今まで OS インストール時に密結合で構築していた perlbal の plugin が .rb として簡単に変更可能、かつテスト可能(これは重要)になったので、rails に到達する前に http のリクエストとレスポンスをプログラマブルに操作が可能となりました。便利。

今後、応用可能な例

  • IP や特定のヘッダや、リファラなどを用いた新しい認証の仕組みの導入
  • redis や yaml ファイルから読み込んだ何かしらのデータを用いたアクセス制御(bot や IP リストとか)
  • あるファイルが作られていたらメンテナンス画面とするような仕組み

夢が広がる〜。

mruby_handler の例

mruby_init_worker
userdata = Userdata.new("memcached_#{Process.pid}")
userdata.memcached = Memcached.new('<%= @memcached_server %>')

ここで nginx の worker ごとに memcached のコネクションを作ります。

mruby_exit_worker
userdata = Userdata.new("memcached_#{Process.pid}")
userdata.memcached.close if userdata.memcached

ここで worker の exit 時にコネクションを切るようにします(もしかしたら、worker process が回収するかもしれない)。

mruby_set
class DaysImageAuth
  def initialize(r, c)
    @r, @c = r, c
  end

  def allowed?
    # 色々アクセスフィルタの処理を書く
    # userdata.memcached.get でセッションデータを読み込んであれこれしたり
    # 最終的にアクセスを通すなら true を返す
  end
end

DaysImageAuth.new(Nginx::Request.new, Nginx::Connection.new).allowed?

(具体的な内容は書けないので参考にはならないけど、使い方のイメージとしてはこんな感じ)

このコードを mruby_set $allow 'your_mruby_code.rb' というように指定すると nginx の $allow 変数に 'true' や 'false' が入るので、その値を見て 50x 返したり何かをしています。

導入時に躓いたポイントや課題

  • mruby のテストを ruby でやっていたけど、組み込みクラスが異なっていることが原因で振る舞いに違いが出たりするので、*.rb のテスト方法は mruby を使って楽にする仕組みを構築する必要がある
  • mruby から memcached に接続してデータを取る、という処理を導入した際に connection をリクエスト毎に作り続けてしまい connection leak を起こして nginx が応答無くなった
  • この問題は worker_rlimit_nofile の設定変更や、mruby_init_worker/mruby_exit_worker の導入で解決した
  • mruby を組み込んだ nginx のビルドはとりあえず vagrant で行ったけど、docker とかでやるともっと楽になるのかなあ。わからん。

まとめ

ngx_mruby はイノベーティブ

RubyPrize 2014 を受賞しました

RubyWorld Conference 2014 で RubyPrize 2014 を受賞しました。推薦していただいた皆さん、選考委員会の皆さん、応援していただいた皆さんありがとうございます。

DSC01281.jpg

今回の受賞に至る経緯や成果については記念講演のスライドを御覧ください。

講演はだいぶカミカミで個人的にはダメだったな〜という感触だったのですが、あちこちで大変良い話だったという感想を頂いたので、伝えたいメッセージは伝わったようで良かったです。引き続き、Ruby で楽しいプログラミングをするために色々やっていこうと思います。