サーバプログラムを書いている時、ソースコードを書き換えるたびにビルド・再起動するのは手間です。 このようなとき、ソースコードの変更を検知して、自動でビルド・再起動してくれるホットリロードツールが便利です。 Go言語でも、realizeやairなど、いくつものホットリロードツールが公開されています。
ホットリロードツールに必要な機能は大きく分けて次の2つです。
- ファイルの更新を監視する
- プログラムを起動・再起動する
拙作のシンプルな汎用ホットリロードツール「arelo」において、これらの機能をどのように実装しているかを解説します。
発表の目的
Go言語は実行するためにコンパイルする必要があります。
開発中の動作確認では、一般的なコマンドラインツールではgo run
コマンドを使えばビルドと実行を一度にできて便利ですが、
サーバプログラムの場合、古いプロセスの停止、ビルドと実行、クライアントからの動作確認、と大幅に手間が増えてしまいます。
このため、古いプロセスのまま動作確認をして、変更が反映されていなくて悩んだ覚えがある人は多いでしょう。
このようなとき、ファイルの変更を検知して再起動してくれるホットリロードツールがあると便利です。 Go言語用にもいくつものホットリロードツールが公開されています。 少し前まではrealizeが定番のツールでしたが、Go Modulesに対応しないまま更新が止まってしまいました。 最近ではairもありますが、私もareloというツールを作っています。
この発表では、ホットリロードツールに必要な機能とその実装方法について、areloでの実例を使って解説します。 これらを理解することで、いざというときには自分自身でホットリロードツールを作れるようになることをゴールとします。
ホットリロードツールに必要な機能
ホットリロードツールの機能の中心は、ファイルの変更を検知し、指定されたプログラムを再起動することです。
ファイル監視では、更新だけでなく、作成や削除、属性変更も検知する必要があります。 加えて、プログラムの動作に関係のあるファイルの変更だけを区別して検知しなくてはなりません。
また、プログラムを再起動するときには既に起動しているプロセスを確実に停止する必要があります。 特定のポートをListenする場合など、古いプロセスが残っていると新しいプロセスはListenできません。 さらに、子プロセスを立ち上げていた場合、それらも含めて停止しないと期待通りに動かなくなってしまいます。
ファイル監視の実装方法
ファイル監視の定番ライブラリとして、fsnotify
があります。
これを用いて特定ディレクトリ以下のすべてのファイルを監視する方法を解説します。
また、プログラムの動作に関係するかどうかはファイル名で判断するのがよくあるパターンです。
ここでは、globパターンでのマッチングとして、標準ライブラリのfilepath.Match()
や拡張パターンの使えるdoublestar
を紹介します。
プログラムのプロセス管理方法
Goから外部プロセスを起動するには標準パッケージのexec
が使えます。
一方でプロセスの停止、特に子プロセスを含めた場合は一筋縄ではいきません。
Unix系OSでのシグナルを利用する方法(参考)の他、
WindowsでのTASKKILL
コマンドを利用した方法を解説します。
加えて、プロセスが停止するまで待つ方法や、待っている間のファイル変更通知を無視する方法なども紹介します。