Может ли супервизор OTP контролировать процесс на удаленном узле?

Я хотел бы использовать диспетчер OTP Erlang в создаваемом мной распределенном приложении. Но мне сложно понять, как такой супервизор может отслеживать процесс, запущенный на удаленном узле. В отличие от функции start_link в erlang, start_child не имеет параметров для указания узла, на котором будет создан дочерний элемент.

Может ли супервизор OTP контролировать удаленного ребенка, и если нет, как я могу добиться этого в erlang?


person Brinley    schedule 24.11.2016    source источник
comment
Я думаю, что рекомендуемый способ - иметь супервизора на каждом узле.   -  person Dogbert    schedule 24.11.2016
comment
@Dogbert Действительно. Фактически, это не просто супервизор, а обычно полная реплика любой системы, которая распространяется, так что рабочие запросы могут передаваться между узлами, не требуя каких-либо существенных изменений в коде.   -  person zxq9    schedule 26.11.2016


Ответы (1)


supervisor:start_child/2 можно использовать на всех узлах.

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

  • Запросчик
  • Наблюдатель
  • Порожденный процесс

Контекст запрашивающей стороны - это тот, в котором вызывается supervisor:start_child/2 - не контекст самого супервизора. Обычно вы предоставляете интерфейс супервизора, экспортируя функцию, которая обертывает вызов в supervisor:spawn_child/2:

do_some_crashable_work(Data) ->
    supervisor:start_child(sooper_dooper_sup, [Data]).

Это может быть определено и экспортировано из модуля супервизора, определено внутри процесса типа «менеджер» в соответствии с " идиома "менеджер по обслуживанию / начальник / рабочие" или что-то в этом роде. Однако во всех случаях этот вызов выполняет какой-либо процесс, кроме супервизора.

Теперь внимательно просмотрите документы Erlang для supervisor:start_child/2 (здесь и R19.1 doc mirror, так как иногда erlang.org по какой-то причине испытывает трудности). Обратите внимание, что тип sup_ref() может быть зарегистрированным именем, pid(), {global, Name} или {Name, Node}. Запрашивающая сторона может быть на любом узле, вызывая супервизор на любом другом узле при вызове с pid(), {global, Name} или {Name, Node} кортежем.

Однако руководитель не просто запускает вещи случайным образом. У него есть child_spec() он выходит из строя, и спецификация сообщает супервизору, что нужно вызвать, чтобы запустить этот новый процесс. Этот первый вызов дочернего модуля выполняется в контексте супервизора и является настраиваемой функцией. Хотя мы обычно называем его как-то вроде start_link/N, он может делать все, что мы захотим, в рамках запуска, включая объявление определенного узла, на котором будет создаваться. Итак, теперь мы получаем что-то вроде этого:

%% Usually defined in the requestor or supervisor module
do_some_crashable_work(SupNode, WorkerNode, Data) ->
    supervisor:start_child({sooper_dooper_sup, SupNode}, [WorkerNode, Data]).

С дочерней спецификацией чего-то вроде:

%% Usually in the supervisor code
SooperWorker = {sooper_worker,
                {sooper_worker, start_link, []},
                temporary,
                brutal_kill,
                worker,
                [sooper_worker]},

Это означает, что первым вызовом будет sooper_worker:start_link/2:

%% The exported start_link function in the worker module
%% Called in the context of the supervisor
start_link(Node, Data) ->
     Pid = proc_lib:spawn_link(Node, ?MODULE, init, [self(), Data]).

%% The first thing the newly spawned process will execute
%% in its own context, assuming here it is going to be a gen_server.
init(Parent, Data) ->
    Debug = sys:debug_options([]),
    {ok, State} = initialize_some_state(Data)
    gen_server:enter_loop(Parent, Debug, State).

Вам может быть интересно, что это за ерунда с proc_lib был для. Оказывается, хотя вызов порождения из любого места в многоузловой системе для инициирования порождения из любого другого места в многоузловой системе возможно, это просто не очень полезный способ сделать бизнес, и поэтому gen_* поведения и даже _ 21_ не имеет метода объявления узла, на котором будет создан новый процесс.

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

Другими словами, мы действительно не хотим порождать воркеров на произвольных узлах, мы действительно хотим перейти на более высокий уровень и запросить выполнение некоторой работы другим узлом, а не на самом деле заботиться о том, как это происходит. Помните, что для создания определенной функции на основе {M,F,A} вызова узел, который вы вызываете, должен иметь доступ к целевому модулю и функции - если у него уже есть копия кода, почему это не копия вызывающего узла?

Надеюсь, этот ответ больше объяснил, чем запутал.

person zxq9    schedule 26.11.2016