Erl*_*abe 5 erlang erlang-otp gen-server
我有一个gen_server注册全局名称的过程,如下所示:
global:register_name(<<"CLIENT_", NAME/binary>>, self()),
Run Code Online (Sandbox Code Playgroud)
gen_server:call另一个进程正在尝试使用如下方式向该进程发送消息:
gen_server:call({global, <<"CLIENT_", NAME/binary>>}, {msg, DATA}),
Run Code Online (Sandbox Code Playgroud)
如果第二次调用发生在第一个进程注册全局名称之前,它将终止并显示:
exit with reason {noproc,{gen_server,call,[{global,<<"CLIENT_122">>},{msg, <<"TEST">>}]}}
Run Code Online (Sandbox Code Playgroud)
仅当全局名称已注册时才进行调用,如果不是则执行其他操作的正确方法是什么?
三件事:
力学
您可以在进行调用之前检查名称是否已在全局注册表中注册,如下所示:
-spec send_message(Name, Message) -> Result
when Name :: term(),
Message :: term(),
Result :: {ok, term()}
| {error, no_proc}.
send_message(Name, Message) ->
case global:whereis_name(Name) of
undefined ->
{error, no_proc};
PID ->
Value = gen_server:call(PID, Message),
{ok, Value}
end.
Run Code Online (Sandbox Code Playgroud)
global:whereis_name/1然而,因为被检查的返回值和通过 的实际调用之间会有几纳秒的时间gen_server:call/2,3,所以你仍然不知道你是否真的只是向一个死进程发送了一个调用,但至少你将它发送到了一个 PID这不会立即使程序崩溃。
另一种方法是使用构造try ... catch,但这是一个非常难以养成的习惯。
稳健的架构
上面的所有内容,请将其放在您的脑海中,但在您的脑海中,如果该名称未注册,您应该希望崩溃。你注册的进程应该是活的,所以你为什么这么偏执?!?如果事情很糟糕,你想知道它们是灾难性的,并让与那次事故相关的一切立即燃烧起来。不要试图在未知的状态下自行恢复,这就是主管的作用。让您的系统在已知状态下重新启动,然后再试一次。如果这是一个用户控制的操作(系统的某个用户,或者网页请求或其他什么),他们会再次尝试,因为他们是多次尝试事物的猴子。如果它是一个自动请求(例如,用户是计算机或机器人),它可以重试或不重试,但在常见情况下将决定权留给它 - 但给它一些失败的指示(错误消息,关闭的套接字等)。
只要您调用的进程在调用期间init/1(在将自己的 PID 返回给其主管之前)注册其名称,并且这种情况总是发生在调用进程处于活动状态或知道要调用的进程之前,那么您就应该'对此没有任何麻烦。如果它由于某种原因崩溃了,那么你的程序就会出现更根本的问题,捕获调用者的崩溃不会对你有帮助。这是鲁棒性工程的基本思想。
构建您的系统,确保被调用者在调用发生之前处于活动状态并已注册,如果它已经死亡,您应该希望调用者也死亡。(是的,我正在打败一匹马,但这很重要。)
代码结构
大多数时候,您不希望有一个定义进程的模块,假设定义foo.erl了一个我们将命名的进程{global, "foo"},对其进行裸调用gen_server:call/2,3,或者gen_server:cast/2旨在用于另一个模块中定义的单独进程(假设bar.erl定义了我们将命名该过程{global, "bar"})。我们想要的是它bar.erl有一个导出的接口函数,并且这个函数就是gen_server:call/2但是发生的地方。
这样,适用于此调用的任何特殊工作(任何其他调用模块也可能需要)都存在于一个位置,并且您可以以除了"bar"传递给它的消息之外还传达一些含义的方式命名进程的接口。
例如,如果 定义的进程bar.erl是一个连接计数器(也许我们正在编写一个游戏服务器并且正在计算连接数),我们可能需要bar.erl负责维护该计数器。因此,每当新用户连接时,进程都会发送一条cast(异步消息) 。bar与其让可能需要执行此操作的每个不同进程定义一些复杂的名称检查,然后裸露消息发送,不如考虑导出一个函数来bar.erl隐藏混乱并命名为有意义的名称,例如bar:notify_connect(). 只需在其他代码中调用它就更容易理解,并且您可以选择如何处理“如果栏不存在怎么办?” 情况就在那里,在一个地方。
关于这一点,您可能想看一下基本的Erlang“服务管理器 -> 工作人员”模式。在许多情况下,命名进程并不是绝对需要的。