概述 GenEvent 是事件處理的通用部分的抽象。 通過 GenEvent ,我們給已有的服務 動態 的添加 事件處理。 GenEevent 和 GenServer 的區別 之前已經介紹了 GenServer ,GenServer 和 GenEvent 的主要區別在於: GenServer 是服務 ...
概述
GenEvent 是事件處理的通用部分的抽象。
通過 GenEvent ,我們給已有的服務 動態 的添加 事件處理。
GenEevent 和 GenServer 的區別
之前已經介紹了 GenServer ,GenServer 和 GenEvent 的主要區別在於:
- GenServer 是伺服器的抽象,除了封裝處理 同步/非同步 事件的方法之外,還封裝了伺服器本身的啟動/停止等方法。
- GenEvent 是事件的抽象,封裝了 同步/非同步 事件的處理方法,GenEvent 可以綁定到任何伺服器上,從而動態的 添加 伺服器的處理方法。
基於上述的區別,GenEvent 和 GenServer 的應用場景也不同。
- GenServer 可以幫助我們快速的創建服務,它類似於一個服務的腳手架,使用 GenServer,構建服務時,只需關註服務本身的業務即可
- GenEvent 可以用於給現有的服務動態添加處理方法,也可以用於抽象多個服務的共通處理
GenEevent 示例
事件管理器
通過 GenEvent 創建一個事件管理器,將此事件管理器添加到現有進程中,現有進程就有了處理相應事件的能力。
簡單示例如下:
- 接收到 :hello 則返回 :world
- 接收到 :world 則返回 :hello
- 接收到 其他消息 則返回 "error msg"
defmodule HelloEvent do
use GenEvent
def handle_event(event, parent) do
case event do
:hello ->
send parent, :world
:world ->
send parent, :hello
_ ->
send parent, "error msg"
end
{:ok, parent}
end
end
測試過程:
# 啟動一個空的事件管理器
iex(1)> {:ok, manager} = GenEvent.start_link
{:ok, #PID<0.87.0>}
# 發送 :hello 消息
iex(2)> GenEvent.sync_notify(manager, :hello)
:ok
# 沒有任何反應,因為事件管理器中沒有任何 handle 來處理消息
iex(3)> flush
:ok
# 給事件管理器增加一個handle,同時將當前進程PID作為事件處理的狀態
iex(4)> GenEvent.add_handler(manager, HelloEvent, self())
:ok
# 發送 :hello 消息
iex(5)> GenEvent.sync_notify(manager, :hello)
:ok
# 事件管理器處理了 :hello 消息,並返回 :world 結果
iex(6)> flush
:world
:ok
# 發送 :world 消息
iex(7)> GenEvent.sync_notify(manager, :world)
:ok
# 事件管理器處理了 :world 消息,並返回 :hello 結果
iex(8)> flush
:hello
:ok
# 發送 :test 消息
iex(9)> GenEvent.sync_notify(manager, :test)
:ok
# 事件管理器對於 :hello 和 :world 以外的消息都返回 "error msg"
iex(10)> flush
"error msg"
:ok
上面測試中用的發送消息的方法都是同步方式 sync_notify ,通過非同步方式 notify 發送消息也是一樣的, GenEvent 的 handle_event 接收同步和非同步的消息。
事件流
事件流就是將 GenEvent 的事件轉入到流中,這樣,就可以通過處理流的方式來處理事件。
比如上面的例子,通過 GenEvent 的 stream ,可以不定義 defmodule HelloEvent 也實現上面的功能。
上述測試過程可以改為如下:
iex(1)> {:ok, manager} = GenEvent.start_link
{:ok, #PID<0.59.0>}
iex(2)> stream = GenEvent.stream(manager)
%GenEvent.Stream{manager: #PID<0.59.0>, timeout: :infinity}
iex(3)>
nil
iex(4)> spawn_link fn ->
...(4)> for x <- stream do
...(4)> case x do
...(4)> :hello -> IO.inspect :world
...(4)> :world -> IO.inspect :hello
...(4)> _ -> IO.inspect "error msg"
...(4)> end
...(4)> end
...(4)> end
#PID<0.71.0>
iex(5)> GenEvent.sync_notify(manager, :hello)
:world
:ok
iex(6)> GenEvent.sync_notify(manager, :world)
:hello
:ok
iex(7)> GenEvent.sync_notify(manager, :test)
"error msg"
:ok
可以看出,我們沒有給 GenEvent 綁定任何的 handler,而是在 GenEvent 的事件流中對所有消息進行了處理。
GenEvent 中事件流的特性是 erlang 中所沒有的。
總結
除了上面用的 handle_event 和 stream, GenEvent 中還有其他的實用的 Functios 和 Callbacks
具體參見:http://elixir-lang.org/docs/stable/elixir/GenEvent.html