2015-12-18 2 views
4

У меня есть простой обработчик событий в эликсира с помощью GenEvent:Запуск нескольких экземпляров одного и того же обработчика событий в эликсира

defmodule myHandler do 
    use GenEvent 
    #Callback 
    def handle_event {:message, x}, state do 
     IO.puts("Message value is #{x}") 
     {:ok, [x|state]} 
    end 
end 

я могу запустить один обработчик и менеджер обычным способом:

{:ok, mgr} = GenEvent.start_link 

myServer.start_link(mgr) 

GenEvent.add_handler(mgr,myHandler, []) 

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

Я пробовал:

Gen.Event.add_handler({mgr, :id1},myHandler, []) 

, не повезло! Вместо я получаю следующее сообщение об ошибке:

** (Mix) Could not start application : exited in: myApp.start(:normal, []) 
** (EXIT) no connection to :id1 

Я новичок в эликсир и так борюсь с документацией немного. Буду признателен, если кто-нибудь покажет мне, как это сделать! Благодарю.

ответ

4

Вы всегда можете иметь более сложное состояние в MyHandler:

defmodule MyHandler do 
    use GenEvent 

    def handle_event({:message, id, message}, {id, messages}) do 
    IO.puts "[ID: #{inspect id}] Message value is #{inspect message}." 
    {:ok, {id, [message | messages]}} 
    end 

    def handle_event(_, state) do 
    {:ok, state} 
    end 
end 

Чтобы фильтровать сообщения по идентификатору, я хотел бы изменить структуру сообщения в:

{:message, id, message} 

Если вы не сделаете этого , каждый обработчик распечатает одно и то же сообщение. Я думаю, именно поэтому вам нужен идентификатор.

Тогда имея id, вы могли бы сделать что-то вроде:

{:ok, manager} = GenEvent.start_link 
MyServer.start_link manager 
GenEvent.add_handler manager, MyHandler, {id, []} 

Как вы можете видеть новое состояние {id :: atom, messages :: list} вместо простого списка сообщений.

Тогда его просто вопрос отправки сообщения:

GenServer.sync_notify manager, {:message, id, message} 

Пример:

Инициализация менеджера:

iex(1)> {:ok, manager} = GenEvent.start_link 
{:ok, #PID<0.75.0>} 

Добавить обработчик:

iex(2)> GenEvent.add_handler manager, MyHandler, {:id0, []} 
:ok 

Testсообщение с ID :id0 и выводит сообщение:

iex(3)> GenEvent.sync_notify manager, {:message, :id0, "Hello"} 
[ID: :id0] Message value is "Hello". 
:ok 

Test сообщение с несуществующим ID :id1 и он ничего не печатает:

iex(4)> GenEvent.sync_notify manager, {:message, :id1, "Hello"} 
:ok 

Там вы идете. Надеюсь, это поможет :)

P.S: Если ваше состояние является слишком сложным, вы всегда можете использовать map:

%{id: id, messages: []} 
+0

спасибо за это. Может быть, я не слишком хорошо понимаю ваш ответ, но я пытаюсь передать одно событие нескольким обработчикам (это все равно); механизм pubsub. Поэтому я не занимаюсь фильтрацией определенных сообщений. Для этого мне нужно использовать тот же модуль, что и обработчик событий, и передать его в виде {Module, id} ... здесь: https://github.com/elixir-lang/elixir/issues/3760 Но это не так Для меня это не имеет никакого значения! –

+1

Хорошо, думаю, я понял. Вышеприведенный код, с фильтрацией и всеми, также может решить вашу проблему. Думаю, вы используете 'id' как имя для канала. В этом случае каждый раз, когда процесс должен подписаться на событие определенного «id», просто добавьте новый обработчик с этим идентификатором. Трюк состоит в том, чтобы дать обработчику также идентификатор, отличный от других обработчиков: 'GenEvent.add_handler manager {MyHandler, handler_id} {id, []}'. Это предотвратит возврат '{: error,: already_present}' –

+0

Да. Я думаю, что наши сообщения просто пересекли! –

4

Так получается, что добавить несколько обработчиков к одному менеджеру нужно что-то вдоль линий:

GenEvent.add_handler(:myManager, {myHandler, :id1}, []) 

У меня был аргумент, который все испортили - благодаря замечательному @true_droid на канале Elixir slack.

Смежные вопросы