unicorn の hook が呼ばれるタイミングを調べてみた
unicorn には特定のタイミングで発火する hook を仕掛ける仕組みがあるが、それらの hook が呼ばれるタイミングを正確にわかっていなかったのでメモ。 いちおう公式のドキュメントはこちらにある。ここで言及する unicorn のバージョンは v5.3.0。
それぞれの hook の説明
after_fork, after_worker_exit, after_worker_ready, before_exec, before_fork の 5 つある。 以下はそれぞれの説明&コードの場所。
after_fork
この hook は fork した後にワーカープロセスから呼ばれる ( init_worker_process )。 init_worker_process は fork したワーカープロセスの中で呼ばれる worker_loop で呼ばれるメソッド。
after_worker_exit
この hook はワーカープロセスが exit する際にマスタープロセスから呼ばれる。 ワーカーが死んだ後、ワーカーの socket などを閉じた後に呼ばれる ( reap_all_worker )。
after_worker_ready
この hook はワーカープロセスがレスポンスを受けられる状態になったところでワーカープロセスから呼ばれる。 fork したワーカープロセスの中で呼ばれる ( worker_loop )。
before_exec
この hook は新しい unicorn のコマンドを exec する直前にマスタプロセスによって呼ばれる。
unicorn は graceful restart などをする際に、呼び出されたときと同じコマンド (例えば unicorn -c unicorn.ru
など) を Kernel#exec する。
その直前に呼ばれる ( reexec )。
before_fork
それぞれのワーカプロセスを fork する前にマスタープロセスから呼ばれる。 Unicorn::Worker オブジェクトを作成したらすぐ呼ばれている ( spawn_missing_workers )。
呼び出しタイミングの確認
ワーカーが 2 つの設定で幾つか実験を行って呼び出しタイミングを見てみた。今回確認する際に使用したソースコードは以下。 もちろんオプションによっては呼び出し順序が変わるので注意。
起動時
- before_fork
- after_fork
- after_worker_ready
マスタープロセスが起動してから 2 つのワーカーに対してそれぞれ before_fork, after_fork, affter_worker_ready が呼ばれている。 マスタープロセスの pid が 46056 でワーカープロセスがそれぞれ、46090 と 46089 になる。 before_fork はマスタープロセスからよばれるので pid は 46056 になる。それ以外はワーカープロセスで呼ばれるのでそれぞれの pid になる。
$ be unicorn -c unicorn.ru I, [2017-05-21T16:26:53.009330 #46056] INFO -- : listening on addr=0.0.0.0:8080 fd=9 I, [2017-05-21T16:26:53.009448 #46056] INFO -- : [before_fork] worker=0 pid=46056 I, [2017-05-21T16:26:53.010518 #46056] INFO -- : [before_fork] worker=1 pid=46056 I, [2017-05-21T16:26:53.012068 #46056] INFO -- : master process ready I, [2017-05-21T16:26:53.012010 #46089] INFO -- : [after_fork] worker=0 pid=46089 I, [2017-05-21T16:26:53.012433 #46089] INFO -- : Refreshing Gem list I, [2017-05-21T16:26:53.013825 #46090] INFO -- : [after_fork] worker=1 pid=46090 I, [2017-05-21T16:26:53.014232 #46090] INFO -- : Refreshing Gem list I, [2017-05-21T16:26:53.121563 #46090] INFO -- : [after_worker_ready] worker=1 pid=46090 I, [2017-05-21T16:26:53.125676 #46089] INFO -- : [after_worker_ready] worker=0 pid=46089
マスタープロセスに SIGINT
- after_worker_exit
すぐにワーカープロセスを殺して after_worker_exit が呼ばれ、その後マスタープロセスも死ぬ。 after_worker_exit はマスタープロセスで呼ばれるので pid は 46056 となる。
I, [2017-05-21T16:29:04.026190 #46056] INFO -- : [after_worker_exit] worker=1 pid=46056 status=pid 46090 exit 0 I, [2017-05-21T16:29:04.026515 #46056] INFO -- : [after_worker_exit] worker=0 pid=46056 status=pid 46089 exit 0 I, [2017-05-21T16:29:04.026615 #46056] INFO -- : master complete
ワーカープロセスに SIGINT
- after_worker_exit
- before_fork
- after_fork
- after_worker_ready
kill -s INT 46651
を実行した後のログ。
マスタープロセスの pid は 46622 なので、after_worker_exit はマスタープロセス内で呼ばれていることがわかる。
そのあと、新しいワーカープロセスを作成するので起動時と同じログになる(ただしワーカーは 1 つ)。
I, [2017-05-21T16:35:10.707660 #46622] INFO -- : [after_worker_exit] worker=1 pid=46622 status=pid 46651 exit 0 I, [2017-05-21T16:35:10.707795 #46622] INFO -- : [before_fork] worker=1 pid=46622 I, [2017-05-21T16:35:10.709798 #46653] INFO -- : [after_fork] worker=1 pid=46653 I, [2017-05-21T16:35:10.710304 #46653] INFO -- : Refreshing Gem list I, [2017-05-21T16:35:10.828349 #46653] INFO -- : [after_worker_ready] worker=1 pid=46653
マスターに SIGUSER2 (graceful restart)
- before_exec
- before_fork
- after_fork
- after_worker_ready
ログとしては Kernel#exec した (1行目の executing
) 後に before_exec がきているが、これはコードでなぜかそういう順番になっているだけで本当の順番は逆 ( https://github.com/defunkt/unicorn/blob/v5.3.0/lib/unicorn/http_server.rb#L455 )。
あとは起動時のときと同じログになる。
I, [2017-05-21T15:45:51.255441 #43971] INFO -- : executing ["/Users/yuta-iwama/src/github.com/ganmacs/playground/ruby/unicorn-hook/vendor/bundle/ruby/2.4.0/bin/unicorn", "-c", "unicorn.ru", {9=>#<Unicorn::TCPSrv:fd 9>}] (in /Users/yuta-iwama/src/github.com/ganmacs/playground/ruby/unicorn-hook) I, [2017-05-21T15:45:51.255758 #43971] INFO -- : [before_exec] I, [2017-05-21T15:45:51.497657 #43971] INFO -- : inherited addr=0.0.0.0:8080 fd=9 I, [2017-05-21T15:45:51.497814 #43971] INFO -- : [before_fork] worker=#<Unicorn::Worker:0x007ffc888f3018> I, [2017-05-21T15:45:51.498835 #43971] INFO -- : [before_fork] worker=#<Unicorn::Worker:0x007ffc888f2ca8> I, [2017-05-21T15:45:51.499294 #43971] INFO -- : master process ready I, [2017-05-21T15:45:51.500538 #43982] INFO -- : [after_fork] worker=#<Unicorn::Worker:0x007ffc888f3018> I, [2017-05-21T15:45:51.500818 #43983] INFO -- : [after_fork] worker=#<Unicorn::Worker:0x007ffc888f2ca8> I, [2017-05-21T15:45:51.501025 #43982] INFO -- : Refreshing Gem list I, [2017-05-21T15:45:51.501152 #43983] INFO -- : Refreshing Gem list I, [2017-05-21T15:45:51.602740 #43983] INFO -- : [after_worker_ready] worker=#<Unicorn::Worker:0x007ffc888f2ca8> I, [2017-05-21T15:45:51.604692 #43982] INFO -- : [after_worker_ready] worker=#<Unicorn::Worker:0x007ffc888f3018>
その他
自分は before_exec と before_fork の呼ばれるタイミングの違いがよくわかっていなかった。 before_exec は restart などをする時に unicorn コマンドを内部で叩く前、before_fork は ワーカープロセスを fork する直前呼ばれるみたい。