2017-09-15 134 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。在很多情況下,命名過程並不是絕對需要的。