gen_event 练习

大概类似于消息管理器

相关资料建议查官方比较好,翻了几篇博文都有错误。

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合适一点。

总结,挺好玩,在项目中有广泛的应用,如果你们项目没用到,多半是被封装成其他模块了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值