Rack用HTTPデーモンの複数起動を制御するスクリプト
RailsでいうところのMongrelClusterのような、複数のHTTPデーモンを起動したり停止したりする仕組みがRackにもあったら便利だなーと思い、Daemonsを使う方法を考えてみました。
以下、サンプルコードに注釈をまじえながら。
まずはrequire
require 'rubygems' require 'rack' require 'daemons'
コマンドラインオプションで渡しそうなものをとりあえず変数で定義
app_dir = '/path/to/app' port = 3000 number = 3 command = 'start' # or 'stop' or 'status' or 'run'
この例では、3つのポート(3000、3001、3002)でlistenします。
commandは、「start」なら起動、「stop」なら停止、「status」ならステータス表示。「run」ならデーモンにせずに1プロセスのみ起動します(開発時に使う)。「restart」は必要性を感じないので実装しません。
なお、実際のコードではOptionParserなどでコマンドラインオプションをパースすることになるでしょう。
Daemonsのオプション
daemon_options = { :ARGV => [command], :dir_mode => :normal, :dir => app_dir, :multiple => true }
:ARGV はDaemonsに渡すパラメータ配列で、1番目の要素が起動や停止をつかさどるコマンドになります。「start」「stop」「status」「run」などが有効な値なので、commandをそのまま渡しています。
:dir_mode と :dir は、ざっくりいうと、pidファイルの作成先を規定する組み合わせです。:dir_mode が「:normal」かつ :dir がディレクトリの絶対パスの場合、そのディレクトリにプロセスファイル(「アプリケーション名+番号」.pid)が作られます。
:dir_mode は「:normal」の他に「:script」と「:system」が選べます(Daemons::Pid.dirのドキュメントを参照)。が、後述の理由のため「:normal」にしました。
:multiple は、同じデーモンの複数起動を許可するかどうかのフラグです。当然、trueにします。
Daemonsの呼び出し
case command when 'start' number.times do |n| Daemons.call(daemon_options) do Rack::Handler::Mongrel.new(MyApp.new, :Port => port + n) end end when 'run', 'stop', 'status' Daemons.run_proc('proc', daemon_options) do Rack::Handler::Mongrel.new(MyApp.new, :Port => port) end end
commandが「start」のとき、複数デーモンを起動するために Daemons.call しています(READMEの「3. Control a bunch of daemons from another application」にあたる)。
commandがそれ以外の場合は、 Daemons.run_proc しています(同「2. Create wrapper scripts that include your server procs」にあたる)。第1引数がアプリケーション名です。これがプロセスファイル名に使われます。
このアプリケーション名を「proc」にしているのには理由があります。Daemons.call の場合、アプリケーション名を渡せません。Daemons内部で「proc」に固定されます(プロセスファイル名が「proc0.pid」「proc1.pid」のようになる)。そこで、Daemons.run_proc 側で渡すアプリケーション名を「proc」にし、Daemons.call に合わせました。
また、Daemons.run_proc のオプションでは、:dir_mode を「:script」にできません。もし「:script」にすると、Daemons内部で「:normal」に置き換えられてしまいます。「:system」にはできますが、そうすると、複数のアプリケーションを動かしたい場合に、プロセスファイルが「/var/run/proc0.pid」「/var/run/proc1.pid」のように一箇所にまとめられてしまい、別々に停止できなくなります。というわけで「:normal」にしました。
改善したい点
というか、これ
Rackじゃなくても使えますよね。
あ、もしかして
それCapistranoだったのかな? ま、いいか。