Erlang OTP дизайн приложения

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

Решение

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

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

1 (Довольно незначительный, но все же важный). Я предлагаю вывести исходный код inets из этого работника и поместить его в код состояния приложения (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. Никто из этих рабочих не запустится, пока вы их явно не запустите.

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).

Что-то одни строчки:

   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, либо вы можете рассмотреть возможность создания отдельного процесса для него, в зависимости от ваших конкретных потребностей.

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

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

Вы на самом деле отслеживаете любое состояние в вашем gen_server?

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

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

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

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

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

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

Другие вопросы по тегам