Skip to content

Instantly share code, notes, and snippets.

@arumons
Last active December 14, 2015 15:08
Show Gist options
  • Save arumons/5105054 to your computer and use it in GitHub Desktop.
Save arumons/5105054 to your computer and use it in GitHub Desktop.
unicorn
How Unicorn Reaps Worker Processes
RubyによるUnixプログラミングを掘り下げるなら、Unicorn web サーバーについても触れておく必要がある。既にこの本でも何度かUnicornについて言及をしてきた。
Unicornの何が凄いのか?Unicornはkernelの機能を限界まで使いこなしている。
コードベースはUnixプログラミングテクニックで一杯だ。
それだけではなく、効率的で信頼性も備えている。GithubやShopifyといったRubyで構築されているWebサイトの多くがUnicornを使っている。
RubyによるUnixプログラミングについてもっと興味がわいならUnicornの詳細を調べてみるといい。
神話上の生き物の内部を探索することで深い理解と新しいアイディアが湧いてくるだろう。
Reaping What?
コードの詳細に飛び込む前にUnicornがどのように動作しているか説明しておこう。ざっくり説明すると
Unicornはpre-forking Webサーバーだ。
これが意味しているのはUnicornをたちあげた後、必要な分のワーカープロセスをUnicornに伝えるということだ。
Unicornはネットワークソケットを初期化し、その後にあなたのアプリケーションをロードする。
次にforkを呼び出してワーカープロセスを作成する。forkの章で説明したmaster-workerパターンを使っている。
Unicornのマスタプロセスはハートビートをワーカープロセスに送り、時間のかかりすぎているプロセスがないかどうかチェックを行う。
下記のコードはUnicornマスタプロセスを終了させるのに使われる。forkの章で説明したように親プロセスはexitの際に子プロセスを
殺すことはしないため、小プロセスは止まることなく動作し続ける。
そのため本体をexit剃る前にUnicornをクリーンアップする必要がある。下記のコードはUnicornのexit処理の一部だ。
このコードを呼び出す前にそれはQUITシグナルを各ワーカプロセスに送り、exit命令を伝える。
下記のコードはワーカープロセスをクリーンアップし、全てのプロセスが適切にexitすることを保証する。
見てみよう。
最初にbeginから始まるこのブロックを取り上げる。
まず目を引くのはエンドレスにloopするbeginブロックの存在だ。無限ループの書き方は色々あるのにこんな書き方になっているのかは
謎だが、とにかく我々は無限ループの中にいて、抜け出すためにはreturnかbreakを呼び出す必要がある。
次のコードは見覚えがあるものだ。Process.waitpid2についてはProcess.waitの章で扱ったが、そのときは
最初の引数にpidを渡し、二番目の引数は何も指定しなかった。
最初の引数にpidを指定すると、waitpidはそのプロセスに対してのみ待つようになるのだった。では-1を渡すとどうなるのだろう?
pidが1未満になることはないが。。
-1を渡すことでどのプロセスでもexitするのを待つようになる。これはこのメソッドのデフォルトオプションもあり、
もしpidを何も渡さなかったら、それは-1を渡したのと同じ事になる。このコードだと2番めの引数も渡す必要があったため、
最初の引数は空にできなかった。なので-1をわざわざ渡しているのである。
しかしあらゆるプロセスを待ちたいのであれば、何故Process.wait2を使わないのだろうか?pidを指定する場合はwaitpid系を使うのが
最もリーダブルであるからだと推測している。上記で述べたように指定した値はデフォルトの値に過ぎないが、
pidを指定して渡すことで目立つようになる。
二番目の引数はフラグでPcocess::WNOHANGを渡すことでブロックしなくなる。このフラグを使うと、exit済みのプロセスがなくても待つことはせず単純にnilが返される。
このコードは若干奇妙だが、実際にはただのreturnステートメントだ。もしwpidがnilならこの行はnilを返す。
ステータスを返すexit済みの子プロセスがない場合にnilが返される。
その状態になったらメソッドはreturnして、そのジョブは終了する。
このコードにはあまり深入りをしない。reexecはUnicorn内部でzeroダウンタイム再起動を処理するのに使われる。
この処理については将来扱うかもしれない。扱わないかもしれない。
proc_nameを見てほしい。これはResquesの章で扱ったproclineメソッドに似ている。
Unicornもまた現在のプロセスの表示名を変えるメソッドを持っている。あなたのソフトウェアの利用者とのコミュニケーションにあたって
重要な要素だ。
Unicornは現在のアクティブなワーカプロセスをWORKERS定数に保持している。WORKERはworkerプロセスのpidをキーとして
Unicorn::Workerのインスタンスを値に持っている。
なのでこの行はWORKERSからワーカプロセスを取り除き、workerインスタンスのcloseメソッドを呼び出している。
closeすることによって対象のプロセスにはハートビートが送られなくなる。
この行はwaitpid2によって返されたstatusを元にlogメッセージを作成している
status.inspectによって着くあられる文字列はどのように表示されるのだろうか?
それは下記のように表示される。
log中にあるworker.nrはUnicorn内部のworkerの番号を表している。
この行は生成したログメッセージをloggerに送っている。sucess?を使うことでログメッセージを送るレベルを振り分けている。
success?メソッドはprocessがexitし、exitコードが0の時にのみtrueになり、それ以外のコードの時はfalseになる。
シグナルによってkillされた場合、successはnilを返す。
これはトップレベルにあるbeginステートメントの一部であり、例外が発生したらbreakしてreturnするようになっている
Errono::ECHILD例外はProcess.waitpid2呼び出し時に子プロセスがひとつも無いときに発生する。
これが発生するということは全てのジョブが終了し、子プロセスの後片付けが済んだということである。
これらのコードがあなたの興味を引き、もっと学んでみたいと思ったならUnicornは格好のリソースだ。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment