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

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

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

参照用 記事

Erlang:メッセージによる手続き呼び出し

Erlangをまだコソコソいじっていたりするのですが:

プロセス間通信を利用する際に、RPC(Remote Procedure Call)みたいな形にすると使いやすいこともあるようです。RPCとは言っても、実際にはリモート(遠隔地)じゃなくて近距離だったりするのですが、、、こういうの、なんと呼べばいいのかな? ローカルRPC -- ひどく語義矛盾してるが、前例があるからこの言葉(LRPCと略記)を使おう、っと

LRPCサーバー

LRPCのサーバー部分はこんな感じでしょう。


start_server() ->
spawn(?MODULE, server_main, []).

server_main() ->
receive
{call, ClientPid, FunctionId, Args} -> % 呼び出しリクエス
case exec(FunctionId, Args) of
{error, Reason} -> ?RETURN(ClientPid, {return, error, Reason});
{ok, Value} -> ?RETURN(ClientPid, {return, ok, Value})
end;
stop -> % プロセス終了命令
io:fwrite("~w: bye bye.\n", [self()]),
exit(ok);
_Other -> io:fwrite("Oops!\n")
end,
server_main(). % 無限ループ

{call, ClientPid, FunctionId, Args}が、LRPCリクエストの“パケット”ってことになります。exec(FunctionId, Args)がサーバー側手順を実行しますが、例として、足し算(sum)と大きな方*1を選ぶ関数(max)。


exec(sum, Args) ->
{ok, lists:sum(Args)};
exec(max, Args) ->
{ok, lists:max(Args)};
exec(_, _) ->
{error, "unknown function."}.

これはえらく安直だけど、どこの誰からLRPCリクエストが来るかわからないときは、引数チェックをきびしくしないとまずいでしょうね。

?RETURNはマクロ呼び出しで、デバッグの都合を考慮して次のように定義しています。


-ifdef(DEBUG).
-define(RETURN(ClientPid, X),
io:fwrite("To: ~w, Returns:~w\n", [ClientPid, X])).
-else.
-define(RETURN(ClientPid, X),
ClientPid!X).
-endif.

-define(DEBUG, true).としてコンパイルすれば*2、次のようにしてサーバー動作を確認できます。


Erlang (BEAM) emulator version 5.5.4 [async-threads:0]

Eshell V5.5.4 (abort with ^G)
1> c(lrpc).
{ok,lrpc}
2> P = lrpc:start_server().<0.1809.0>
3> P!{call, self(), sum, [2, 3]}.
To: <0.1802.0>, Returns:{return,ok,5}
{call,<0.1802.0>,sum,[2,3]}
4> P!{call, self(), max, [2, 3]}.
To: <0.1802.0>, Returns:{return,ok,3}
{call,<0.1802.0>,max,[2,3]}
5>

ただし、文字列が数値リストで表示されたりします(Erlangの文字列とは、実は数値リストですから)。


5> P!{call, self(), foo, [2, 3]}.
To: <0.1802.0>, Returns:{return,error,[117,110,107,110,111,119,110,32,102,117,11
0,99,116,105,111,110,46]}
{call,<0.1802.0>,foo,[2,3]}
6>
この数値リストはエラーメッセージなのですけどね(苦笑)。


6> io:fwrite("~s\n", [[117,110,107,110,111,119,110,32,102,117,110,99,116,105,111
,110,46]]).
unknown function.
ok
7>

LRPCクライアント

LRPCのクライアント側はこんな感じ。


call(ServerPid, FunctionId, Args) ->
ServerPid! {call, self(), FunctionId, Args},
receive
{return, ok, Value} -> {ok, Value};
{return, error, Reason} -> {error, Reason}
after 2*1000 -> {error, "timeout."}
end.

sum(ServerPid, N, M) -> call(ServerPid, sum, [N, M]).
max(ServerPid, N, M) -> call(ServerPid, max, [N, M]).

-define(DEBUG, true).は外してコンパイルし直して実行してみます。


7> c(lrpc).
{ok,lrpc}
8> f().
ok
9> P = lrpc:start_server().<0.1832.0>
10> lrpc:sum(P, 2, 3).
{ok,5}
11> lrpc:max(P, 2, 3).
{ok,3}
12>

*1:正確には、小さくない方です。

*2:コンパイラerlcのオプション-Dで定数定義を与えることもできます。