Catalyst::Engine::HTTP::Restarter における自動再起動サーバーの実装

前の話の続き。新しい server.pl は -r オプションをつけたときに、自動的にファイルの修正を検知して再起動してくれる機能が付いてます。

いままでの server.pl だと、デーモン上で perl が永続化してるためにモジュールを書き換えてもそれが再読込されず、一度 ^C で止めてまた起動とかする必要がありました。で、それが必要なくなったと。先日 Rails を試しにいじってたんですが、Rails 付属の WEBrick なサーバーは、クラスの更新も検知してくれてスゲー便利だったので、server.pl がこの仕様になったのはすごくいいです。

どういう実装でこれを実現してるのかなーと思って見てたんですが、やっぱりそこは Catalyst、ちょっと面白い実装になってました。手元にある 5.33 からだと

の二つのクラスが追加されてます。

Watcher は File::Modified を使って、指定したディレクトリの中の .pm ファイルの mtime を監視するクラスで、変更を検知した際にモジュールのシンタックスチェックもしてくれるユーティリティのような役割。特に Catalyst に結合してる感じではないです。

一方の Catalyst::Engine::HTTP::Restarter は Catalyst::Engine::HTTP を継承しています。

  • app_server.pl から Catalyst::Engine::HTTP::Restarter->run が呼ばれる
  • fork する
  • 親は $self->NEXT::runCatalyst::Engine::HTTP->run へ redispatch されデーモンになり HTTP リクエストを待機
  • 子は Watcher インスタンスを作って無限ループの中でモジュールの変更を監視する。
  • 子はモジュールの変更を関知するとソケットで親デーモンの待機ポートに接続し、HTTP で 'RESTART' というメソッドを送信し終了する
  • 親は RESTART メソッドを受け取ると自分自身を実行しているスクリプトを exec して再度自分を実行する

という流れで処理が進んでいきます。ファイルの変更を感知するための処理を NEXT と fork で別プロセスに分離してるのと、子から親へのシグナルに HTTP を使ってるあたりが面白いですね。