2015-10-15 42 views
2

嘗試在項目中使用OTP風格並獲得一個OTP接口問題。什麼解決方案更受歡迎/美麗?Erlang嘗試使gen_server:調用很多響應

我有什麼:

  1. Web服務器與mochiweb
  2. 一個過程,什麼滋生許多(1000-2000)的孩子。 孩子們包含狀態(netflow-speed)。處理代理消息給兒童並創建新的兒童,如果需要的話。

到Mochiweb我有一個頁面,所有演員的速度,乳清如何製造:

nf_collector ! {get_abonents_speed, self()}, 
    receive 
     {abonents_speed_count, AbonentsCount} -> 
      ok 
    end, 
%% write http header, chunked 
%% and while AbonentsCount != 0, receive speed and write http 

這不是拉攏風格,我怎麼能理解。解決方案:

  1. 在API同步函數中獲取所有具有速度和返回列表的速度的請求。但我想立即寫給客戶。 API函數的
  2. 一種觀點回調:

    nf_collector:get_all_speeds(fun (Speed) -> Resp:write_chunk(templater(Speed)) end) 
    
  3. 返回迭代: 一個get_all_speeds的結果將與接收塊的功能。每次調用它將返回{ok, Speed},最後返回{end}

get_all_speeds() -> 
    nf_collector ! {get_abonents_speed, self()}, 
    receive 
     {abonents_speed_count, AbonentsCount} -> 
      ok 
    end, 
    {ok, fun() -> 
     create_receive_fun(AbonentsCount) 
    end}. 

create_receive_fun(0)-> 
    {end}; 

create_receive_fun(Count)-> 
     receive 
      {abonent_speed, Speed} -> 
       Speed 
     end, 
     {ok, Speed, create_receive_fun(Count-1)}. 

+1

什麼是實際問題?你是否在實現任何選項時遇到困難,如果是的話,你應該問一些與之相關的東西。否則,這將主要取決於您的使用情況。 –

+0

我同意亞當看到這更像一個設計問題,但沒有足夠的任何建議的信息。爲什麼有1000-2000個孩子包含國家。爲什麼有進程返回計數和發出調用而不是返回這些孩子,並讓調用者決定要做什麼?什麼是讀/寫比率?非功能性要求是什麼?低延遲或吞吐量更重要嗎?它是主要功能還是系統其餘部分有多大?等等。沒有額外的信息,對我來說沒有多大意義。 –

+0

寫這個問題的原因很簡單:erlang給出了編寫基於actor的程序的簡單方法,OTP給予了標準化。首先,我寫的程序沒有OTP,理解程序邏輯很複雜。添加OTP後,它變得平坦和簡單。 在這裏,我得到了一個更復雜的行爲,同步/異步調用。我問道,如果其他erlang開發者會遇到類似問題,他們會選擇哪一種。 或者這個問題成爲不好的設計和答案是 - 做到這一點簡單,沒有1000(或更多)演員 – kolko

回答

1

菌種你從上司的孩子「:

-module(ch_sup). 
-behaviour(supervisor). 
-export([start_link/0, init/1, start_child/1]). 
start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). 
init([]) -> {ok, {{simple_one_for_one}, [{ch, {ch, start_link, []}, transient, 1000, worker, [ch]}]}}. 
start_child(Data) -> supervisor:start_child(?MODULE, [Data]). 

與ch_sup啓動:start_child/1(數據是什麼)。

實現你的孩子作爲gen_server:

-module(ch). 
-behaviour(gen_server). 
-record(?MODULE, {speed}). 

...

get_speed(Pid, Timeout) -> 
    try 
     gen_server:call(Pid, get, Timeout) 
    catch 
     exit:{timeout, _} -> timeout; 
     exit:{noproc, _} -> died 
    end 
. 

...

handle_call(get, _From, St) -> {reply, {ok, St#?MODULE.speed}, St} end. 

您現在可以使用超級獲得運行列表孩子和查詢他們,儘管你必須接受孩子在獲得兒童名單之間死亡的可能性並打電話給他們,顯然一個孩子可能因爲某種原因而活着,但沒有迴應,或者有錯誤迴應等。

上面的get_speed/2函數返回{ok,Speed}或死亡或超時。您仍然可以根據您的應用需求進行適當的過濾。列表理解容易,這裏有一些。

只是速度:

[Speed || {ok, Speed} <- [ch:get_speed(Pid, 1000) || Pid <- 
    [Pid || {undefined, Pid, worker, [ch]} <- 
     supervisor:which_children(ch_sup) 
     ] 
    ]]. 

PID和速度元組:

[{Pid, Speed} || {Pid, {ok, Speed}} <- 
    [{Pid, ch:get_speed(Pid, 1000)} || Pid <- 
     [Pid || {undefined, Pid, worker, [ch]} <- 
       supervisor:which_children(ch_sup)] 
     ] 
    ]. 

所有結果,包括超時和孩子,你有他們在這之前死 '死亡' 的結果:

[{Pid, Any} || {Pid, Any} <- 
    [{Pid, ch:get_speed(Pid, 1000)} || Pid <- 
     [Pid || {undefined, Pid, worker, [ch]} <- 
       supervisor:which_children(ch_sup)] 
     ] 
    ]. 

在大多數情況下,您幾乎可以肯定除了速度之外不需要其他任何東西,因爲wh你打算怎麼處理死亡和超時?你希望那些死去的人會被主管重新塑造,所以當你知道這個問題時,問題或多或少是固定的,超時和任何錯誤一樣,都是一個單獨的問題,要以你看到的任何方式來處理適合...雖然沒有必要將故障修復邏輯與數據檢索邏輯混合起來。

現在,所有這些,我認爲你在你的文章中得到的問題,但我不太清楚,1000的超時是爲每個電話,並且每個電話是另外,對於1000個1秒超時的孩子,可能需要1000秒才能產生結果。製作時間超時1ms的可能是答案,但做正確是一個比較複雜一點:

get_speeds() -> 
    ReceiverPid = self(), 
    Ref = make_ref(), 
    Pids = [Pid || {undefined, Pid, worker, [ch]} <- 
      supervisor:which_children(ch_sup)], 
    lists:foreach(
     fun(Pid) -> spawn(
      fun() -> ReceiverPid ! {Ref, ch:get_speed(Pid, 1000)} end 
      ) end, 
     Pids), 
    receive_speeds(Ref, length(Pids), os_milliseconds(), 1000) 
. 

receive_speeds(_Ref, 0, _StartTime, _Timeout) -> 
    []; 
receive_speeds(Ref, Remaining, StartTime, Timeout) -> 
    Time = os_milliseconds(), 
    TimeLeft = Timeout - Time + StartTime, 
    receive 
     {Ref, acc_timeout} -> 
      []; 
     {Ref, {ok, Speed}} -> 
      [Speed | receive_speeds(Ref, Remaining-1, StartTime, Timeout)]; 
     {Ref, _} -> 
      receive_speeds(Ref, Remaining-1, StartTime, Timeout) 
    after TimeLeft -> 
     [] 
    end 
. 

os_milliseconds() -> 
    {OsMegSecs, OsSecs, OsMilSecs} = os:timestamp(), 
    round(OsMegSecs*1000000 + OsSecs + OsMilSecs/1000) 
. 

這裏每次打電話都催生了一個不同的過程和收集的答覆,直到「主超時」或者他們有全部收到。代碼已經基本上被剪切粘貼從各種作品,我已經躺在,並手動編輯和搜索替換,匿名它,並刪除多餘,所以它可能大多是compilable質量,但我不承諾我沒有破壞任何東西。

+0

感謝您的大回答! 您可以對每個孩子進行同步呼叫。我想爲每個孩子做結果異步調用。 (或者如果使用OTP規範,並行同步調用,比如你通過產生新的演員(我害怕產生1000個孩子,你告訴產生1000 * :))) 爲什麼我認爲標準電話是壞主意: 1.迭代所有的孩子和按順序呼叫 - 很慢 2.如果派生演員爲每個同步呼叫 - 太多演員 也許,進行同步呼叫,調用結果{ok,pid}和之後 - 發送速度,呼叫者獲得所有pid,添加手錶來監控死亡。 – kolko

+0

我的解決方案可能是:我與主管進行同步呼叫,他爲我的pid保存兒童的列表,發送給所有兒童get_speed(MyPid)並回答{ok,ChildsCount}。 小孩送我他的速度。如果孩子死亡,他的pid被保存在列表中,主管會發送給我{err,child_die}或somephing,因爲我可以計算出答案的數量,他不會回答。 但是錯誤: 1.當主管可以刪除保存的列表? 2.如果我死了,孩子們可以理解什麼需要停止發送速度? – kolko

+0

我重新閱讀OTP文檔,並認爲我的問題是「奇怪」,並且需要更同步(並且更慢:))來執行此操作,但是需要通過OPT原則。或者改變我的應用程序的架構。 我在這裏有一些代碼(全都很髒,現在沒有OTP了,我試着學習並重寫它)如果有人從我的問題中產生興趣,歡迎:https://github.com/kolko/erl_nf_collector 我標記你的回答是正確的,堅決反對(並重新閱讀OPT文件),幫助我解決我的問題的奇怪設計。 謝謝大家! – kolko