2011-10-19 15 views
2

首先我想道歉,我提供了這麼多的信息,以儘可能清楚問題是什麼。請讓我知道是否還有什麼需要澄清的。Erlang插座直到第二個setopts纔會收到{active,once}

(運行二郎R13B04,內核2.6.18-194,CentOS的5.5)

我有一個很奇怪的問題。我有以下代碼傾聽和處理插座:

%Opts used to make listen socket 
-define(TCP_OPTS, [binary, {packet, raw}, {nodelay, true}, {reuseaddr, true}, {active, false},{keepalive,true}]). 

%Acceptor loop which spawns off sock processors when connections 
%come in 
accept_loop(Listen) -> 
    case gen_tcp:accept(Listen) of 
    {ok, Socket} -> 
     Pid = spawn(fun()->?MODULE:process_sock(Socket) end), 
     gen_tcp:controlling_process(Socket,Pid); 
    {error,_} -> do_nothing 
    end, 
    ?MODULE:accept_loop(Listen). 

%Probably not relevant 
process_sock(Sock) -> 
    case inet:peername(Sock) of 
    {ok,{Ip,_Port}} -> 
     case Ip of 
     {172,16,_,_} -> Auth = true; 
     _ -> Auth = lists:member(Ip,?PUB_IPS) 
     end, 
     ?MODULE:process_sock_loop(Sock,Auth); 
    _ -> gen_tcp:close(Sock) 
    end. 

process_sock_loop(Sock,Auth) -> 
    try inet:setopts(Sock,[{active,once}]) of 
    ok -> 
     receive 
     {tcp_closed,_} -> 
      ?MODULE:prepare_for_death(Sock,[]); 
     {tcp_error,_,etimedout} -> 
      ?MODULE:prepare_for_death(Sock,[]); 

     %Not getting here 
     {tcp,Sock,Data} -> 
      ?MODULE:do_stuff(Sock,Data); 

     _ -> 
      ?MODULE:process_sock_loop(Sock,Auth) 
     after 60000 -> 
      ?MODULE:process_sock_loop(Sock,Auth) 
     end; 
    {error,_} -> 
     ?MODULE:prepare_for_death(Sock,[]) 
    catch _:_ -> 
     ?MODULE:prepare_for_death(Sock,[]) 
    end. 

這整個安裝工程奇妙正常,並一直工作在過去的幾個月。服務器作爲具有長時間tcp連接的消息傳遞服務器運行,並且平均擁有大約100k個連接。但是現在我們試圖更加嚴格地使用服務器。我們正在將兩個長期連接(將來可能更多)連接到erlang服務器,並且每個連接每秒創建幾百個命令。在通常情況下,這些命令中的每一個都產生了一個新的線程,這個線程可能會從mnesia讀取一些信息,並根據這些信息發送一些消息。

當我們嘗試測試這兩個命令連接時,會出現奇怪的現象。當我們打開命令流時,任何新的連接都有大約50%的掛起機會。例如,如果使用netcat連接併發送字符串「blahblahblah」,服務器應立即返回錯誤。在這樣做的時候,它不會在線程外進行任何調用(因爲它所做的一切都是試圖解析命令,因爲blahblahblah不是命令,所以命令會失敗)。但是大約有50%的時間(當兩個命令連接運行時)輸入blahblahblah導致服務器只是坐在那裏60秒,然後返回該錯誤。

在試圖調試這個我拉了wireshark。 tcp握手總是立即發生,當客戶端發送第一個數據包(netcat)時,它會立即進行確認,告訴我內核的tcp堆棧不是瓶頸。我唯一的猜測是問題在於process_sock_loop函數。它有一個接收將在60秒後返回到函數的頂部,並再次嘗試從套接字獲取更多信息。我最好的猜測是,下面發生的事情:

  • 連接時,線程移動到process_sock_loop
  • {活躍,一旦}設置
  • 線程接收,但沒有得到數據,即使它的存在線程追溯到process_sock_loop頂部
  • {活躍,一旦}再次
  • 設置這一次的數據來通過,事情進行一個
  • 後60秒s正常

爲什麼這是我不知道的,當我們關閉這兩個命令連接時,一切都恢復正常,問題消失。

任何想法?

回答

2

很可能是你的第一個調用設置{活躍,一旦}未能因您的通話產卵和您的電話之間的競爭條件,以controlling_process

這將是間歇性的,可能是基於主機負載。

做這件事時,我通常產生一個功能上類似塊: {走,襪子}

,然後調用襪子上你的循環,設定{活躍,一旦}。

所以你要改變接受者產卵,設置controls_process然後Pid! {take,Sock}

這種情況。 注意:當你不是控制進程時,我不知道{active,once}調用是否實際拋出,如果不存在,那麼我剛纔所說的是有道理的。

+0

感謝您的回覆!我最終試圖將我的erlang版本升級到R14,並且在升級到CentOS 6的過程中,問題在此之後還沒有出現。問題消失的唯一原因可能是由於新版本在CPU上的負載較小,一旦它再次出現,它會再次出現,但我無法再次發生。 –

+0

原來升級並未完全解決問題。然而,在這個小小的修復中加入了。謝謝! –

+0

如果有人檢查此答案,[ERL-90](http://bugs.erlang.org/browse/ERL-90)包含有關此掛起的實際原因的更多信息。 –