Erlang: лучший способ для одноэлементного gen_server в кластере erlang?

Параметр:

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

Процесс является частью супервайзера. Проблема в том, что запустить супервизор на втором узле не удается, потому что gen_server уже запущен и зарегистрирован глобально на первом узле.

Вопросы):

  • Можно ли проверить, зарегистрирован ли процесс уже глобально внутри функции start_link gen_server, и в этом случае вернуть {ok, Pid} уже запущенного процесса вместо запуска нового экземпляра gen_server?
  • Верно ли, что таким образом один процесс будет частью нескольких супервизоров, и если один процесс выйдет из строя, все супервизоры на всех других узлах попытаются перезапустить процесс. Первый супервизор создаст новый процесс gen_server, а все остальные супервизоры снова будут связаны с этим процессом.
  • Должен ли я использовать какой-то global:trans() внутри функции start_link gen_server?

Пример кода:


start_link() ->
    global:trans({?MODULE, ?MODULE}, fun() ->
        case gen_server:start_link({global, ?MODULE}, ?MODULE, [], []) of
            {ok, Pid} -> 
                {ok, Pid};
            {error, {already_started, Pid}} ->  
                link(Pid), 
                {ok, Pid};
            Else -> Else
        end     
    end).


person Rumpelstilz    schedule 16.12.2010    source источник


Ответы (2)


Если вы вернете {ok, Pid} чего-то, на что вы не ссылаетесь, это смутит руководителя, который полагается на возвращаемое значение. Если вы не собираетесь, чтобы супервизор использовал это как функцию start_link, вам это сойдет с рук.

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

Один из способов, которым я создавал глобальные синглтоны в прошлом, — это запускать процесс на всех узлах, но при этом один из них (тот, который выиграет глобальную гонку за регистрацию) будет ведущим. Другие процессы следят за мастером и, когда мастер выходит, пытаются стать мастером. (И опять же, если они не выигрывают регистрационную гонку, то следят за pid того, кто выиграл). Если вы сделаете это, вам придется самостоятельно обрабатывать регистрацию глобального имени (т. е. не использовать функцию gen_server:start({global, ...), потому что вы хотите, чтобы процесс запускался независимо от того, выиграет он регистрацию или нет, просто в каждом случае он будет вести себя по-разному.

Сам процесс должен быть более сложным (он должен работать как в режиме master, так и в режиме non-master), но он быстро стабилизируется и не создает большого количества спама в логах при попытках запуска супервизора.

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

person archaelus    schedule 16.12.2010
comment
Думаю, это именно то, о чем я говорю. Я добавил пример блока кода к моему вопросу, чтобы показать мой предполагаемый подход. Можете ли вы объяснить, почему мне не следует использовать gen_server:start({global, ..)? - person Rumpelstilz; 17.12.2010
comment
Ваш код выглядит довольно разумно. Вероятно, вам также не нужна глобальная транзакция — какой-то процесс выиграет гонку за регистрацию. Мой ответ неверен, я также хотел предоставить собственный глобальный преобразователь конфликтов, поэтому не использовал встроенную поддержку {global, Name}. - person archaelus; 17.12.2010
comment
Сегодня мне не хватило кофе - помимо конфликтов имен есть веская причина, по которой я не пользовался {глобальной, ... поддержкой. - person archaelus; 17.12.2010
comment
Спасибо, что копнули глубже. Но я не уверен, что понял вашу точку зрения. Разве часть мониторинга (то, что делают ваши не главные процессы) уже не обрабатывается супервизором на каждом узле? - person Rumpelstilz; 17.12.2010

Как насчет превращения gen_server в приложение и использования распределенных приложений?

person puzza007    schedule 16.12.2010
comment
Проблема, которую я вижу, заключается в том, что вам нужен дополнительный исходный код оболочки приложения, а имена узлов должны быть известны и заранее настроены в конфигурации ядра. - person Rumpelstilz; 16.12.2010
comment
+1 это путь. Если вы хотите создать это самостоятельно, это: а) трудно сделать правильно без условий гонки и б) вы можете взять в качестве примера источник управления распределенным приложением. И вы можете создать файл конфигурации из erlang, например. запрашивая все узлы из узла генератора конфигурации, если вы не хотите, чтобы имена узлов были статическими. OTOH, когда узлы являются динамическими, существует дополнительная вероятность сбоя при поиске узлов (откуда вы знаете, что у вас есть все узлы, которые вам нужны?) - person Peer Stritzinger; 16.12.2010