Part28:GenServer

海未「日本語の情報源が減ってきました・・・」
希「まあ、予想はしとったけど・・・海未ちゃんが音を上げるとはね」
海未「いえ、読めないわけではないのですが、そうは言ってもなかなか大変でした」

海未「ではいきます。前回作ったAgentを使ったプロセス管理と、その前のプロセスの説明を思い出してください」
凛「思い出すことがたくさんあるよ~」
海未「プロセスにはProcess.registerで名前を付けられました。しかし前回の例では名前付けはしていませんでしたね」
希「Agent.start_linkが返してきたbucketをそのまま使ってたね」
海未「そこで、そのbucketに名前を付けたいとしましょう」

海未「KV.Bucketの実装をこうしてから」

海未「使う側をこのようにすると、:card1という名前でbucketにアクセスできます。スコアマッチの対戦カードだと思ってください」
希「これだと、bucketを持って回らなくても名前を指定すればどこからでもアクセスできるんやね」
海未「しかし問題があるのです。名前はアトムでなくてはなりません。ということは、ユーザーが入力した名前を付けることはできないのです」
凛「そっか、文字列をアトムには変換できないんだね」
海未「そこで、個々の対戦カードの名前を管理するサーバプロセスを、GenServerを使って作ってみましょう」

海未「ここではbucketの名前に文字列を使えるように、簡単なレジストリサーバを作っています」
凛「これもHashDictへの出し入れ?」
海未「そうですね。ただ、ここで耐障害性の話が出てきます。今回のGenServerを使った実装では、レジストリの整合性を保証するために個々のプロセスを監視する機能を導入していきます」

海未「GenServerを使って実装したサーバは、Client APIとServer Callbackの2種類の関数群から成り立っています」
希「コメント打ってあるやつやね」
海未「Client APIが、クライアントコードから呼び出される関数群です。Server Callbackは、Client APIの呼び出しを受けてサーバ内部で呼び出される関数群ですね」

海未「start_link関数は、これまでと同じようにレジストリサーバのプロセスを開始する関数です」
凛「__MODULE__って何?」
海未「現在のモジュールの名前、この場合だとKV.Registryになります」

海未「lookupとcreateは、bucketを検索する機能と新しいbucketを登録する機能です。中ではサーバに対してcallとcastという関数を呼び出していますが、これについては後で詳しく説明します」

海未「Server Callbackの方ですが、initはGenServer.start_linkから呼び出される初期化用関数です」
希「ってことは、間接的にClient APIのstart_linkから呼び出されてるってこと?」
海未「そうなりますね。Callbackというのは、Client APIからGenServerを通して間接的に呼び出される関数なんです」

海未「Server Callbackにhandle_callとhandle_castがありますね。これは、Client APIからcallとcastを呼び出したときに呼び出される関数です」
希「ふむふむ、そこで名前を受け取ってHashDictに登録なり検索なりをやっている、と」
凛「callとcastって何が違うの?」
海未「callは同期、castは非同期という違いがあります。callの場合、GenServerはhandle_callの処理が終わるまで待ちますが、castの場合は待ちません」
凛「そっか、検索は結果待たないといけないけど、登録は待たなくてもいいんだ!」

海未「Scorematchのクライアントコードは、見たままだと思いますので説明は割愛しますね」

海未「ではクライアントコードを少し書き換えてみましょう」

海未「Agent.stop bucketでbucketを終了させているのですが、その後でもlookupで死んだプロセスを取得できてしまいます。これは望ましくないですね」
希「まあ確かに、プロセス死んだらレジストリからも消えてくれないとゴミだらけになるね」
海未「ここで監視の仕掛けを実装して、プロセスが死んだときにレジストリから削除されるようにしてみましょう」

海未「このように、死んだプロセスを取得しようとするとエラーになります」
希「どうやったん?」
海未「Process.monitorがポイントです。この関数は、プロセスを監視するモニターリファレンスというものを返すので、それをrefsというHashDictで管理するように修正しています」
希「handle_castでbucket作るところやね」
海未「こうしておくと、プロセスが死んだときにhandle_infoが:DOWN付きで呼び出されます。そのタイミングで、bucketとモニターリファレンスを削除しています」
凛「自分でProcess.alive?とかしなくてもいいんだね」
海未「これがElixirの監視システムの便利なところですね」

海未「GenServerの基礎はこんな感じです。プロセス監視やメッセージのやりとりはElixir/OTPが面倒を見てくれますから、アプリケーションのロジックだけに集中できますね」

希「さて次回」
凛「凛は、しばらく入院しようと思うので・・・」
希「おっと、英語嫌だから逃げようったって無駄やで?」
海未「わかりました。西木野総合病院を手配しましょう」
希「能力は、天候操作・・・」
海未「実験台待ったなしですね」
凛「わー!やる!頑張ってやるにゃ!」


真姫「うちの病院そんなことしないわよ?」


LINEで送る
Pocket


返信を残す

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