簡體   English   中英

Erlang OTP應用程序設計

[英]Erlang OTP application design

當我將一些代碼轉換為OTP應用程序時,我正在努力解決OTP開發模型。

我本質上是一個網絡爬蟲,我只是不知道在哪里放置實際工作的代碼。

我有一個主管,開始我的工人:

-behaviour(supervisor).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).

init(_Args) ->          
  Children = [
    ?CHILD(crawler, worker)
  ],  
  RestartStrategy = {one_for_one, 0, 1},
  {ok, {RestartStrategy, Children}}.

在此設計中,Crawler Worker負責執行實際工作:

-behaviour(gen_server).

start_link() ->
  gen_server:start_link(?MODULE, [], []).

init([]) ->
  inets:start(),        
  httpc:set_options([{verbose_mode,true}]), 
  % gen_server:cast(?MODULE, crawl),
  % ok = do_crawl(),
  {ok, #state{}}.

do_crawl() ->
  % crawl!
  ok.

handle_cast(crawl}, State) -> 
  ok = do_crawl(),
  {noreply, State};

do_crawl產生了大量的進程和請求,用於處理通過http進行爬網的工作。

問題,最終是:實際抓取應該在哪里發生? 從上面可以看出,我一直在試驗觸發實際工作的不同方式,但仍然缺少一些對於整合事物的方式至關重要的概念。

注意:為簡潔起見,省略了一些OTP管道 - 管道就在那里,系統全部掛在一起

如果我的問題出錯,我會道歉。

我可以提出一些建議,以指導您正確的方向(或我認為是正確的方向:)

1(相當小,但仍然很重要)我建議將inets啟動代碼從該worker中取出並將其放入應用程序statup代碼(appname_app.erl)。 據我所知,你正在使用鋼筋模板,所以你應該有這些。

2現在,到必要的部分。 為了充分利用OTP的管理程序模型,假設您想要生成大量的爬蟲,那么使用simple_one_for_one主管而不是one_for_one會很有意義 (請閱讀http://www.erlang。 org / doc / man / supervisor.html了解更多詳細信息,但必不可少的部分是:simple_one_for_one - 一個簡化的one_for_one管理程序,其中所有子進程都是動態添加相同進程類型的實例,即運行相同的代碼。)。 因此,您不必僅啟動一個監督過程,而是實際指定一個排序的“模板” - 關於如何啟動正在執行的工作進程。 這種類型的每個工作人員都是使用主管開始的:start_child / 2 - http://erldocs.com/R14B01/stdlib/supervisor.html?i=1&search=start_chi#start_child/2 在您明確啟動它們之前,這些工作者都不會啟動。

2.1根據爬蟲的性質,您可能需要評估工人需要的重啟策略類型。 現在,在您的模板中,您將其設置為永久性(但您有一種不同類型的受監督子項)。 以下是您的選擇:

 Restart defines when a terminated child process should be restarted. A permanent child process should always be restarted, 
 a temporary child process should never be restarted and a transient child process should be restarted only if it terminates 
 abnormally, i.e. with another exit reason than normal.

所以,你可能希望有類似的東西:

 -behaviour(supervisor).
 -define(CHILD(I, Type, Restart), {I, {I, start_link, []}, Restart, 5000, Type, [I]}).

 init(_Args) ->          
     Children = [
          ?CHILD(crawler, worker, transient)
     ],  
     RestartStrategy = {simple_one_for_one, 0, 1},
    {ok, {RestartStrategy, Children}}.

我冒昧地建議對這些孩子進行暫時重啟,因為這對於這類工作人員是有意義的(如果他們沒有完成工作則重新啟動,如果他們沒有正常完成則不重啟)

2.2一旦您處理了上述項目,您的主管將處理任意數量的動態添加的工作流程; 它將監視並重新啟動(如果需要)每個,這為您的系統穩定性和可管理性增加了很多。

3現在,一個工人流程。 我假設每個爬蟲都有一些特定的狀態,它可能在任何給定的時刻。 出於這個原因,我建議使用gen_fsm(有限狀態機,更多關於它們的信息,請訪問http://learnyousomeerlang.com/finite-state-machines )。 這樣,您動態添加到主管的每個gen_fsm實例都應該在init / 1中向自己發送一個事件(使用http://erldocs.com/R14B01/stdlib/gen_fsm.html?i=0&search=send_even#send_event/2 )。

單獨的東西:

   init([Arg1]) ->
       gen_fsm:send_event(self(), start),
       {ok, initialized, #state{ arg1 = Arg }}.

   initialized(start, State) ->
       %% do your work
       %% and then either switch to next state {next_state, ...
       %% or stop the thing: {stop, ...

請注意,您的工作可以包含在此gen_fsm過程中,也可以考慮根據您的特定需求為其生成單獨的過程。

如果認為有必要,您可能希望為爬網的不同階段提供多個州名。

無論哪種方式,希望這將有助於以某種OTP方式設計您的應用程序。 如果您有任何問題,請告訴我,如果有必要,我很樂意添加一些東西。

你真的跟蹤gen_server中的任何狀態嗎?

如果答案是肯定的,那么看起來你正在以正確的方式做事。 請注意,由於消息是序列化的,因此通過上述實現,您無法同時進行兩次爬網。 如果您需要並發抓取,請在此處查看我的問題的答案。

如果答案是否定的,那么你可以可能溝服務器和監督者,只是使用應用程序模塊的任何初始化代碼,看到這里

最后, lhttpcibrowse被認為是更好的替代品。 我在廣告服務器上使用lhttpc進行制作,效果很好。

我對此問題的解決方案是查看Erlang Solutions“作業”應用程序,該應用程序可用於調度作業(即請求頁面)並讓單獨的系統處理每個作業,綁定並發性等等。

然后,您可以將新網址提供給進程crawl_sched_mgr ,該進程會過濾網址,然后生成新作業。 你也可以讓請求者自己做。

如果你不想使用工作,Yurii的建議是要走的路。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM