このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

Erlang実験室:間接メッセージング・サーバー

Erlangでは、PID(プロセスID)がプロセスを識別・参照する基本手段ですが、register(Name, Pid) とすると、名前(記号アトム)でプロセスを識別できるようになります。例えば、myprocがプロセスに付けた名前だとして、myproc ! Msg.としてメッセージ送信ができます。

この「名前を指定しての送信」機構を、自前で作ってみました。それが、間接メッセージング・サーバー(IMS; Indirect Messaging Server)です。同様な機能を再実装(車輪の再発明)しても無意味のようですが、IMSは、Erlang組み込みの名前付け方式とは少し違います。

  1. 名前に(アトムではなく)文字列を使う。(これはまー、どうでもいいことですが。)
  2. 名前に束縛されるプロセスを交替(入れ替え/置き換え)してもよい。
  3. 名前に束縛されるプロセスが一時的に存在しなくてもよい。
  4. 名前をゾンビ状態(存在はしても機能しない)にできる。

名前とプロセスが1対1に対応してないところがミソです。

IMSのインターフェースと内部状態

次が、IMSのインターフェース定義(IDL)です。


/*
* ims -- Indirect Messaging Server
* 仕様記述のために書いたIDL
*/

/* 型の定義 */
// Erlangのデータ型を模倣
typedef unsigned long pid[3]; // Erlang pid型 インチキ
typedef any term; // Erlang term型 イイカゲン

// 完了コード
enum CompletionCode {OK, ERROR};
// 一般的な結果の型 ( `ok | {error, Reason}' in Erlang )
struct Result {
CompletionCode completed;
term reason; // ERRORのときだけ使用
};

/* 主要インターフェース */
interface imsMain {
// 名前(トークン)を登録する
Result register(in string token);
// 名前を登録し、プロセスを束縛する
Result register2(in string token, in pid proc);
// 名前にプロセスを束縛する
Result bind(in string token, in pid proc);
// 名前とプロセスの束縛を解放する
Result unbind(in string token);
// 名前を無効化する
Result purge(in string token, in term signal);
// 名前の登録を削除する
Result unregister(in string token);

// 名前で指定した相手にメッセージを送る
Result send(in string token, in term message);
};

今回、IDLコンパイラを使ったわけではありませんが、それでも、IDLを書いて整理するのはよい方法だと思います。実際のErlangモジュールims.erlは、もっとたくさんの関数をexportしてますが、上のIDLに挙げた関数は状態遷移を引き起こします。IMSのサーバープロセスは、名前(トークンと呼んでいる)ごとに次のように状態遷移しながら動作します。

ソースコード

IMSのコーディングで感じたこと

有限状態マシンのコーディングは退屈なものになりますが、Erlangで書いてもやっぱり単調で、でも注意力が必要な辛い作業になります。あんまり手書きするもんじゃないですね。gen_fsm(Generic Finite State Machine Behaviour)(→http://www.erlang.org/doc/man/gen_fsm.html)という有限状態マシンのフレームワークを使えば楽だったかもしれません(ちゃんと調べてないので確かなことは言えませんが)。

gen_serverフレームワークは使ってます。が、お行儀が悪いところがあって、gen_serverの状態データを使わずに、直接プロセス辞書を操作してます。Erlangでは、プロセス辞書を使うのは dirty thing なんだそうです。場合分けのところで、Otherwise -> confused(Otherwise)を入れているのもdefensiveな匂いがしてイヤだったんですが、心配でツイ入れてしまった。([追記]関数の呼び側で正しい引数を渡す責任があるので、Otherwiseみたいな処理はすべきではない -- というのがErlangでの普通の文化習慣のようです。ただし、不明なメッセージのフラッシュをOtherwiseで行うことは推奨されています。[/追記]
defensiveといえば、実行時の型チェックをどのくらいやるか悩みますね。やり出すときりがないので、可読性の邪魔にならないときだけパターンマッチとして追加してますが、さじ加減は難しい。

IMSは何をするのか

ims:register("foobar", P).とすると、プロセスPが名前"foobar"で登録されます。ims:send("foobar", hello).として、名前"foobar"を通してプロセスPにメッセージ送信ができます。… それだけ? それだけです。

全然面白くありませんね。HTTPリクエストに対して眠るだけのmod_sleepもバカみたいでしたが、IMSもバカみたい。でもね、バカみたいにつまらないものを2つ組み合わせると面白くなることもあるんですよ。そのことは近々話す予定です。