大概类似于消息管理器
相关资料建议查官方比较好,翻了几篇博文都有错误。
http://erlang.org/doc/man/gen_event.html
首先新建一个世界消息源,也就是上帝视角的我们,用来控制发送消息。
world_console.erl
-module(world_console).
-vsn(1).
%% API
-export([start/0, stop/0, log/1, report/1, release/1, add_handler/1, which/0, swap/2]).
-define(ServerName, island).
start() ->
%% 注册一个gen_event 消息管理器
gen_event:start_link({local, ?ServerName}).
add_handler(ModuleName) ->
%% 注册消息监听
%% [] 即 event_test:init([])
gen_event:add_handler(?ServerName, ModuleName, []).
stop() ->
gen_event:stop(?ServerName).
log(E) ->
%% 通知事件管理器有新消息
gen_event:notify(?ServerName, {log, E}).
report(ModuleName) ->
%% 执行rpc获得数据
gen_event:call(?ServerName, ModuleName, report).
release(ModuleName) ->
%% 删掉消息监听
gen_event:delete_handler(?ServerName, ModuleName, release).
which() ->
gen_event:which_handlers(?ServerName).
swap(Han1, Han2) ->
gen_event:swap_handler(?ServerName, Han1, Han2).
然后再弄一个 哨兵 小旋风
batman1.erl
-module(batman1).
-behaviour(gen_event).
-vsn(1).
%% API
-export([init/1, handle_event/2, handle_call/2, terminate/2, code_change/3]).
-define(Name,"小旋风").
%% 初始化函数,第一参是初始化的进程字典数据
init(Arg) ->
io:format("~ts 已上线 ~n",[?Name]),
{ok, Arg}.
%% 通知事件
%% 第一参是消息,第二参是当前进程字典数据
handle_event({log, _E}, List) ->
io:format("~ts: 害,这种事就不要找我了,一边去 ~n",[?Name]),
{ok, List}.
%% rpc回调,第二参是返回值,第三参是当前进程字典数据
handle_call(report, List) ->
io:format("~ts: 报告老大,今天巡山完毕,啥事都没有 ~n",[?Name]),
{ok, List, List}.
%% gen_event:stop(zy_event). 触发此函数
terminate(stop, List) ->
io:format("~ts: 小岛崩塌 QAQ 把值钱的东西带走开溜 =~p ~n", [?Name,List]),
ok;
%% gen_event:delete_handler(zy_event, event_test, report). 触发此函数
terminate(release, List) ->
io:format("我【~ts】回家睡觉了 已关机勿扰 =~p ~n", [?Name, List]),
ok.
code_change(OldVsn, State, Extra) ->
io:format("zy ~p ~p =~p ~n", [?MODULE, ?LINE, {OldVsn, State, Extra}]),
{ok, State}.
再弄一个哨兵 大旋风 这样才能体现消息通知给了每个模块
batman2.erl
-module(batman2).
-behaviour(gen_event).
-vsn(1).
%% API
-export([init/1, handle_event/2, handle_call/2, terminate/2, code_change/3]).
-define(Name,"大旋风").
%% 初始化函数,第一参是初始化的进程字典数据
init(Arg) ->
io:format("~ts 已上线 ~n",[?Name]),
{ok, Arg}.
%% 通知事件
%% 第一参是消息,第二参是当前进程字典数据
handle_event({log, E}, List) ->
io:format("~ts: 什么?还有这种事发生,赶快记下,等会通知老大 ~n",[?Name]),
{ok, [E|List]}.
%% rpc回调,第二参是返回值,第三参是当前进程字典数据
handle_call(report, List) ->
io:format("~ts: 报告老大今天发生了这些事 ~ts ~n",[?Name,List]),
{ok, List, List}.
%% gen_event:stop(zy_event). 触发此函数
terminate(stop, _List) ->
io:format("~ts: 与小岛同存亡 ~n", [?Name]),
ok;
%% gen_event:delete_handler(zy_event, event_test, report). 触发此函数
terminate(release, List) ->
io:format("我【~ts】回家睡觉了 已关机勿扰 =~p ~n", [?Name, List]),
ok.
code_change(OldVsn, State, Extra) ->
io:format("zy ~p ~p =~p ~n", [?MODULE, ?LINE, {OldVsn, State, Extra}]),
{ok, State}.
然后shell里一顿乱搞
1> world_console:start(). %% <- 创建小岛 消息管理器
{ok,<0.6933.0>}
2> world_console:add_handler(batman1). %% <- 把小旋风加入监听列表,有消息后会转发给小旋风
小旋风 已上线
ok
3> world_console:add_handler(batman2). %% <- 把大旋风加入监听列表,有消息后会转发给大旋风
大旋风 已上线
ok
4> world_console:log("一只猴子进来了"). %% <- 发生消息
大旋风: 什么?还有这种事发生,赶快记下,等会通知老大
小旋风: 害,这种事就不要找我了,一边去
ok
5> world_console:log("猴子怒吼hasaki"). %% <- 发生消息
大旋风: 什么?还有这种事发生,赶快记下,等会通知老大
ok
小旋风: 害,这种事就不要找我了,一边去
6> world_console:which(). %% <- 手下活着的小弟
[batman2,batman1]
7> world_console:report(batman1). %% <- 小旋风报告
小旋风: 报告老大,今天巡山完毕,啥事都没有
[]
8> world_console:report(batman2). %% <- 大旋风报告
大旋风: 报告老大今天发生了这些事 猴子怒吼hasaki一只猴子进来了
[[29492,23376,24594,21564,104,97,115,97,107,105],
[19968,21482,29492,23376,36827,26469,20102]]
9> world_console:release(batman1). %% <- 让小旋风下线,新消息不会告诉给小旋风
我【小旋风】回家睡觉了 已关机勿扰 =[]
ok
10> world_console:swap({batman2,release},{batman1,[]}).%% <- 让大旋风正常下线,小旋风上线
我【大旋风】回家睡觉了 已关机勿扰 =[]
小旋风 已上线
ok
11> world_console:add_handler(batman2).
大旋风 已上线
ok
12> world_console:stop(). %% <- 小岛崩塌,消息管理器停止,各自处理好最后的清理工作
小旋风: 小岛崩塌 QAQ 把值钱的东西带走开溜 =[]
大旋风: 与小岛同存亡
ok
关于gen_event:swap_handler 官方给的说明是
gen_event:swap_handler
gen_event:swap_sup_handler -----> Module1:terminate/2
Module2:init/1
执行Module1的terminate 然后执行 Module2 的init
虽然不难理解,但swap字面意思是交换,感觉这个行为用replace合适一点。
总结,挺好玩,在项目中有广泛的应用,如果你们项目没用到,多半是被封装成其他模块了。