在非客户端,rpc:call(Node,serveur,inverse,[a]) 为 Node rpc 服务器构建一条消息并等待响应。
在unserver端,RPC服务器接收到消息并启动一个进程来调用函数serveur:inverse(a)。
inverse 函数向执行指令1/a 的serveur:recever() 发送消息并崩溃。
因此,无法将回复消息发送回 inverse。反函数将永远等待答案,以及非客户端节点上的 rpc:call,因为您没有定义任何超时。
您可以在反函数中定义超时:
inverse(X) ->
ownServer!{self(),1/X}
receive
{ownServer, Reply} -> {ok,Reply}
after 100 -> % define a timout of 100 ms
{error,timeout}
end.
另外,使用rpc:call(Node, Module, Function, Args, Timeout)在远程过程调用中使用超时是个好主意
在上一篇文章中,您尝试使用 trap_exit 标志获得响应。那里有几个错误。首先正如@legoscia 所解释的,如果出现错误,退出消息将发送到任何链接的进程。第二个是您期望您的进程将继续执行其代码。发生错误时,进程立即停止,系统发出退出消息,该消息将终止或将被所有链接的进程接收,具体取决于标志 trap_exit 的值。
我写了一个符合你预期的版本:
-module(serveur).
-export([start/0,recever/0,inverse/1]).
-export([do_op/3]).
%%%%
start() ->
Pid=spawn(serveur,recever,[]),
register(ownServer, Pid).
%%%%
recever() ->
process_flag(trap_exit,true),
receive
stop -> stopped;
{'EXIT',_,_} -> recever(); % necessary to throw the {EXIT,_,normal} messages
{From, Op, X} ->
spawn_link(serveur, do_op, [self(),Op,X]),
receive
Reply -> From ! {ownServer, Reply}
end,
recever()
end.
do_op(From, inverse, X) ->
From ! {result,1/X}.
%%%%
inverse(X) ->
ownServer!{self(), inverse, X},
receive
{ownServer, Reply} ->Reply
end.
事实上,这段代码的工作方式或多或少类似于catch 语句,这正是您想要做的,也是您应该在那里使用的。在erlang中,当意外发生时让进程崩溃是一个非常好的主意,特别是使用Erlang OTP机制,但是当错误很可能时(例如用户界面)我认为它更适合使用catch或try/catch在正确的级别。
[编辑]
您想完全了解系统的行为,这很酷。要回答你的问题,很抱歉,我从不使用 rpc,也不知道它适合哪些情况。
对于这种情况,我使用允许集群节点之间通信的全局库(请参阅erlang distribution from learnyousomeerlang a very good site to learn and understand erlang)。
正如您所说,您解决问题的方式使用了大量代码(我不确定它现在是否可以在本地运行)。在我看来,这是因为标志 trap_exit 不是用于此用途,而是用于 OTP 主管树和所有 otp 行为(请参阅What is OTP from the same site)。在您的情况下,您应该使用 catch 语句并添加超时来处理可能的错误。这是处理错误参数和重载服务器的代码。我添加了一些接口来模拟不同的用例。
-module(serveur).
-export([start/0,recever/0,inverse/1,lock/0,unlock/0,stop/0,wait10s/0]).
%%%%
start() ->
Pid=spawn(serveur,recever,[]),
register(ownServer, Pid).
%%%%
recever() ->
receive
{From, X} ->
From ! {ownServer,(catch 1/X)},
recever();
waitForUnlock ->
ok = wait_for_unlock(),
recever();
stop -> server_stopped
end.
%%%%
inverse(X) ->
ownServer ! {self(),X},
receive
{ownServer, {'EXIT',{Reply,_}}} ->
{error,Reply};
{ownServer, Reply} ->
{ok,Reply}
after 100 ->
{error,timeout}
end.
%%%% use this interface to simulate an overloaded server
lock() ->
ownServer ! waitForUnlock.
%%%% use this interface to unlock the server
unlock() ->
ownServer ! unlock.
%%%% use this interface to simulate a very long answer from server
wait10s() ->
timer:sleep(10000),
iAmAwake.
%%%% use this interface to stop the server
stop() ->
ownServer ! stop.
%%%% private function used to hang the server
wait_for_unlock() ->
receive
unlock -> ok
end.
本地节点测试
(unserveur@MyPc)1> c(serveur).
{ok,serveur}
(unserveur@MyPc)2> serveur:start().
true
(unserveur@MyPc)3> serveur:inverse(2).
{ok,0.5}
(unserveur@MyPc)4> serveur:inverse(a).
{error,badarith}
(unserveur@MyPc)5> serveur:lock().
waitForUnlock
(unserveur@MyPc)6> serveur:inverse(2).
{error,timeout}
(unserveur@MyPc)7> serveur:inverse(a).
{error,timeout}
(unserveur@MyPc)8> serveur:unlock().
unlock
(unserveur@MyPc)9> serveur:inverse(2).
{ok,0.5}
(unserveur@MyPc)10> serveur:wait10s().
iAmAwake
(unserveur@MyPc)11> serveur:stop().
stop
(unserveur@MyPc)12> serveur:inverse(2).
** exception error: bad argument
in function serveur:inverse/1 (serveur.erl, line 60)
(unserveur@MyPc)13>
和(几乎)来自客户端节点的相同测试
(unclient@MyPc)1> net_adm:ping(unserveur@MyPc).
pong
(unclient@MyPc)2> rpc:call(unserveur@MyPc,serveur,start,[]).
true
(unclient@MyPc)3> rpc:call(unserveur@MyPc,serveur,inverse,[2]).
{ok,0.5}
(unclient@MyPc)4> rpc:call(unserveur@MyPc,serveur,inverse,[a]).
{error,badarith}
(unclient@MyPc)5> rpc:call(unserveur@MyPc,serveur,lock,[]).
waitForUnlock
(unclient@MyPc)6> rpc:call(unserveur@MyPc,serveur,inverse,[2]).
{error,timeout}
(unclient@MyPc)7> rpc:call(unserveur@MyPc,serveur,inverse,[a]).
{error,timeout}
(unclient@MyPc)8> rpc:call(unserveur@MyPc,serveur,unlock,[]).
unlock
(unclient@MyPc)9> rpc:call(unserveur@MyPc,serveur,inverse,[2]).
{ok,0.5}
(unclient@MyPc)10> rpc:call(unserveur@MyPc,serveur,wait10s,[]).
iAmAwake
(unclient@MyPc)11> rpc:call(unserveur@MyPc,serveur,wait10s,[],1000).
{badrpc,timeout}
(unclient@MyPc)12> rpc:call(unserveur@MyPc,serveur,stop,[]).
stop
(unclient@MyPc)13> rpc:call(unserveur@MyPc,serveur,inverse,[2]).
{badrpc,{'EXIT',{badarg,[{serveur,inverse,1,
[{file,"serveur.erl"},{line,60}]},
{rpc,'-handle_call_call/6-fun-0-',5,
[{file,"rpc.erl"},{line,197}]}]}}}
(unclient@MyPc)14>