2017-09-15 181 views
3

我有一个gen_server过程,注册一个全局命名这样的全局名称:gen_server:呼吁未注册

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

另一个进程试图发送此过程中使用gen_server:call这样的消息:

exit with reason {noproc,{gen_server,call,[{global,<<"CLIENT_122">>},{msg, <<"TEST">>}]}} 
gen_server:call({global, <<"CLIENT_", NAME/binary>>}, {msg, DATA}), 

如果第二个呼叫发生前的第一个过程注册全局名称,它与死亡

只有在全局名称为注册时才能进行呼叫的正确方法是什么,如果不是,请执行其他操作?

回答

3

三件事:

  1. 如何防范这种调用(力学)。
  2. 为什么你一般应该不想守卫调用(健壮的架构)。
  3. 把你的接口放到这个函数(代码结构)的地方。

力学

可以检查名称是否与全球注册表中注册做这样的调用之前:

-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()。只需在其他代码中调用此代码就会更容易理解,并且您可以选择应该如何处理这个“如果不存在该怎么办?”就在那里,在一个地方。

在该说明中,您可能需要查看基本的Erlang "service manager -> worker" pattern。在很多情况下,命名过程并不是绝对需要的。