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-адреса, а затем порождает новые задания. Вы также можете позволить запрашивающим сделать это самим.
Если вы не хотите работать, советуем Юрию - это путь.