Erlang OTP дизайн приложения - PullRequest
8 голосов
/ 12 марта 2011

Я немного пытаюсь разобраться с моделью разработки 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 оставлены для краткости - все здесь и все системы висят вместе

Ответы [ 3 ]

11 голосов
/ 12 марта 2011

Я прошу прощения, если я неправильно понял ваш вопрос.

Несколько предложений, которые я могу сделать, чтобы направить вас в правильном направлении (или то, что я считаю правильным направлением:)

1 (Довольно незначительный, но все же важный). Я предлагаю вывести исходный код inets из этого работника и поместить его в код состояния приложения (appname_app.erl).Насколько я могу судить, вы используете шаблоны арматурных стержней, так что вы должны иметь их.

2 Теперь о важных деталях.Чтобы в полной мере использовать модель OTP supervisor , предполагая, что вы хотите создать большое количество искателей, было бы разумно использовать вместо них simple_one_for_one супервизоровиз one_for_one (для получения более подробной информации прочитайте http://www.erlang.org/doc/man/supervisor.html, но важной частью является: simple_one_for_one - упрощенный супервизор one_for_one, где все дочерние процессы являются динамически добавляемыми экземплярами одного и того же типа процесса, т.е. выполняются одинаковокод.).Таким образом, вместо того, чтобы запускать только один процесс для контроля, вы фактически укажите своего рода «шаблон» - о том, как запускать рабочие процессы, которые выполняют реальную работу.Каждый работник такого типа запускается с помощью supervisor: 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, либо вы можете подуматьсоздание отдельного процесса для него, в зависимости от ваших конкретных потребностей.

Возможно, вы захотите иметь несколько имен состояний для разных этапов сканирования, если это будет сочтено необходимым.

В любом случае, надеждаэто поможет в разработке вашего приложения в некотором роде. Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы, я буду рад добавить что-нибудь, если это необходимо.

3 голосов
/ 12 марта 2011

Действительно ли вы отслеживаете состояние в вашем gen_server?

Если ответ положительный, похоже, вы делаете все правильно. Обратите внимание, что, поскольку сообщения сериализуются, в описанной выше реализации вы не можете одновременно выполнять два обхода. Если вам нужно одновременное сканирование, см. Ответ на мой вопрос здесь .

Если ответ отрицательный, вы можете отказаться от сервера и супервизора и просто использовать модуль приложения для любого кода инициализации, как показано здесь .

Наконец, lhttpc и ibrowse считаются лучшими альтернативами inets. Я использую lhttpc в производстве на своих рекламных серверах, и он отлично работает.

2 голосов
/ 12 марта 2011

Моим решением этой проблемы было бы заглянуть в приложение Erlang Solutions для «заданий», которое можно использовать для планирования заданий (т. Е. Запроса страниц) и позволить отдельной системе обрабатывать каждое задание, связывать параллелизм и так далее.

Затем вы можете передавать новые URL-адреса в процесс crawl_sched_mgr, который фильтрует URL-адреса и затем создает новые задания.Вы также можете позволить запрашивающим сделать это самим.

Если вы не хотите использовать работу, совет Юрия - это путь.

...