gpt4 book ai didi

erlang - gen_server:调用未注册的全局名称

转载 作者:行者123 更新时间:2023-12-01 01:48:43 26 4
gpt4 key购买 nike

我有一个gen_server进程注册了这样的全局名称:

global:register_name(<<"CLIENT_", NAME/binary>>, self()),


另一个进程正在尝试使用 gen_server:call向该进程发送消息,如下所示:

 gen_server:call({global, <<"CLIENT_", NAME/binary>>}, {msg, DATA}),


如果第二个调用在第一个进程注册全局名称之前发生,它将死于:

exit with reason {noproc,{gen_server,call,[{global,<<"CLIENT_122">>},{msg, <<"TEST">>}]}}


仅当全局名称为register时才进行呼叫的正确方法是什么?

最佳答案

三件事:


如何维护此呼叫(力学)。
为什么您通常不希望监视此呼叫(可靠的体系结构)。
将接口放置到此功能(代码结构)的位置。


机械学

您可以在进行如下调用之前检查名称是否已在全局注册表中注册:

-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.


但是,由于检查 global:whereis_name/1的返回值与通过 gen_server:call/2,3进行的实际调用之间相差几纳秒,所以您仍然不知道是否实际上只是将调用发送给无效进程,但至少您将其发送到不会立即导致程序崩溃的PID。

做到这一点的另一种方法是使用 try ... catch构造,但这是一个非常棘手的习惯。

稳健的架构

上面的所有内容都放在您的脑海中,但是如果未注册此名称,则应该在脑海中崩溃。您的注册过程应该还很活跃,所以为什么您如此偏执?!?如果情况不好,您想以灾难性的方式知道它们是不好的,并让与该崩溃相关的所有信息立即消失。不要试图在未知状态下自行恢复,这就是主管的目的。让您的系统以已知状态重新启动,然后再进行一次操作。如果这是用户控制的操作(系统的某些用户,网页请求等),则它们将重试,因为它们是猴子,它们反复尝试多次。如果是自动请求(例如,用户是计算机或机器人),它可以重试还是不重试,但在通常情况下,该决定由其决定,但会给出失败指示(错误消息,封闭的插座等)。

只要您正在调用的进程在其 init/1调用期间注册其名称(在其将自己的PID返回给其主管之前),并且这总是在调用进程处于活动状态或知道要调用的进程之前发生,您应该不会有任何麻烦。如果它由于某种原因而崩溃,那么您的程序会遇到更多基本问题,赶上调用者的崩溃不会对您有所帮助。这是鲁棒性工程中的基本思想。

对系统进行结构设计,以确保被叫方可以存活并在发生呼叫之前进行注册,并且如果该被叫方死亡,则还应该使呼叫者也死亡。 (是的,我打败了一匹死马,但这很重要。)

代码结构

大多数时候,您不想拥有一个定义流程的模块,例如 foo.erl定义了一个我们将命名为 {global, "foo"}的流程,对 gen_server:call/2,3gen_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()。只需在其他代码中调用它就更容易理解,并且您可以选择如何处理“如果bar不存在怎么办?”一处就在那里。

关于这一点,您可能想看一下基本的 Erlang "service manager -> worker" pattern。在许多情况下,绝大多数情况下都不需要命名进程。

关于erlang - gen_server:调用未注册的全局名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46230598/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com