Part30:Supervisor

2015/9/21

希「黒澤ルビィ生誕祭2015」
海未「・・・あの」
凛「誰にゃ?」
希「ウチにはな、スクールアイドルの未来が見えるんよ。きっとあの子も、遠からずAqoursの1人としてラブライブに名前を刻むで」
海未「・・・はぁ」
希「ウチらと違って最初からわりと売れそうな曲とビジュアルやしな」
海未「よく分かりませんがその発言は不穏当な気がします!」


本編ここから

希「さて、ここまででスコアマッチメッセージサーバの名を借りて汎用キーバリューストアを作ってきたわけや」
海未「その言い方も身も蓋もないですが」
希「たくさんのプロセスの間でメッセージを送り合って、そのライフサイクルをレジストリで管理し、イベントの発生に反応する方法も勉強してきた」
凛「けっこう難しかったけど、これでいろんなアプリケーションが作れるんだね」
希「いーや、まだや。ErlangやElixirの耐障害性の恩恵を、ウチらはまだ受けてないんや」

希「Elixirの文化はlet it crashや。何かおかしなことになると、プロセスはさっさとクラッシュするようになってる」
凛「そういえば、例外の面倒を見たりする仕掛けがあるんだったにゃ」
希「そう。それがSupervisorや」

希「Supervisorの主な役割は、プロセスを監視して死んだら再起動させることや。まずはSupervisorを作るところを見てみよか」

希「lib/kv/supervisor.exに」

希「次に、クライアントコード。とりあえずはbucket作ってlookupするだけや」

希「実行すると・・・」

海未「クライアントコードでGenEventやKV.Registryをstart_linkしていたのがなくなっていますね」
希「そのへんもSupervisorのコードに組み込んだんよ」
海未「Supervisorだけ起動すると必要なものは揃うようになったのですね」
希「Supervisorは管理対象の生殺与奪を握っとかないといかん」
凛「@manager_nameとか@registry_nameって何?」
希「この書き方はまだ出てきとらんかったな。モジュール内のどこからでも参照できる変数や」
凛「ほえー」
希「initstart_linkから呼ばれるコールバック関数や。ここで、どのプロセスを管理下に置くかを設定して、プロセスを起動して監視を開始してる」
海未「childrenというリストが管理対象・・・でしょうか」
希「そうやね。ここではGenEventとKV.Registryが管理対象。後で便利なようにそれぞれに名前を付けてる」
海未「PIDを持ち歩かなくてもプロセスにアクセスできるのですね」
希「最後にsuperviseでプロセスの管理を開始する。ここのstrategyは色々あるんやけど、:one_for_oneは管理対象のプロセスが1つ死んだら、代わりに新しいプロセスを1つ起動する、っていう動きをする」
海未「障害回復を自動的にやってくれるのですね。それは頼もしいです」

希「さて。この時点では、bucketが死ぬとそれを管理してるレジストリも死ぬ。これは分かるな?」
海未「KV.RegistryからKV.Bucketをstart_linkしているからですね」
希「そうや。start_linkしてる時点で一蓮托生なんよ。で、さっきのSupervisorが管理してるのはKV.Registryや。つまり・・・」
凛「bucketが1つでも死んだらレジストリが丸ごと再起動されるんだよね」
希「そう。そして再起動されたレジストリの中身は・・・」
凛「はっ、空になるにゃ」
希「今の状態だと、Supervisorはbucketの面倒は見てないことになるね。そこで、bucketが死んでもレジストリを巻き込まないようにしてみようか」

希「まずKV.Bucket用のSupervisorを用意する。lib/kv/bucket/supervisor.exに」

希「この子はKV.Bucketを管理するようにinitで設定してる。restart: :temporaryはbucketが死んでも自動的には再起動しないということや」
凛「あれ?再起動しちゃだめなの?」
希「ここで再起動しちゃうと、レジストリに登録されないやん?だから、bucket作るときは必ずレジストリを通すようにするんや」
凛「そっか、bucket死んだらレジストリから消えちゃうから、レジストリを通して起動しないといけないんだね」
海未「:simple_one_for_oneというのは」
希「1種類しか子プロセスを持てないという以外は:one_for_oneとほぼ同じや」

希「次にKV.Registryのコードを直して、今のSupervisorを使うようにする」

希「handle_caststart_bucketを使うようにしてる以外は、stateにSupervisor入れて持ち運ぶようにしてるだけや」

希「仕上げや。このKV.Bucket.SupervisorとさっきのKV.Supervisorをつなげてやって、Supervision Treeというのを作ってやる。lib/kv/supervisor.exで子として追加してやるんや」

希「これで、クライアントコードはさっきと同じままで同じように動くけど、bucketもSupervisorの管理下にある」
凛「ほんとに管理されてるかわかんないにゃ」
希「それを確かめるために、クライアントコードに手を入れてみよか」

希「前の方は置いといて、まずbucketを2つ作ってやる。で、card1をProcess.exitで殺してやる」
海未「最初の状態なら、これでレジストリごと死ぬ・・・」
希「はずだったのである」

凛「おおっ、レジストリも他のbucketも生きてるにゃ!」
希「これで当初の目的は達したわけや」

希「最後に、いちいちKV.Supervisor.start_linkとかやらんでもええようにしてみよう。mix.exsを開くと」

希「真ん中あたりにこんなのがある。これはMixの回で出てきたアプリケーションの設定ファイルの一部や。これを」

希「こうする。これは、アプリケーション起動時のコールバックを指定してるんや」
海未「起動時に何か自動的に実行するイメージでしょうか」
希「そうやね。modで実行対象モジュールを指定するんやけど、このKVはこれから用意する」

希「lib/kv.exを作って、コールバック関数を実装する」

希「mix.exsで指定したモジュールのstart関数が呼び出される。ここではSupervisorを起動してやってる」
海未「ここで起動しているということは・・・」
希「そ。クライアントコードからはKV.Supervisor.start_linkは消しても動く、というか消さないと動かん」

希「Supervisorについては以上や。ややこしくなってきたけど、この概念を覚えんと本格的なアプリケーションは書けへん」
海未「OTP周りはもうしばらく続きそうですね。では次回は、私がETSの説明をしましょう」


LINEで送る
Pocket


返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です