Gen_event行为分析和实践
1.简介
Gen_event实现了通用事件处理,通过其提供的标准接口方法以及回调函数,在OTP里面的事件处理模块是由一块通用的事件管理器和任意数量的事件处理器,并且这些事件处理器可以动态的添加和删除。一个事件可以用来记录error,alarm,info, warning等信息。一个事件管理器可以安装0,1,N个事件处理器,当一个事件管理器接受到一个事件的通知时,这个事件将会被所有的已安装的事件处理器处理(如图)。
事件管理器实质上是{Module, State}组成的列表,每个Module是一个事件处理器,State是该事件处理器的内部状态。Gen_server接口函数与回调函数之间的关系:
gen_event moduleCallbackmodule
-------------------------------
gen_event:start_link ----->-
gen_event:add_handler
gen_event:add_sup_handler ----->Module:init/1
gen_event:notify
gen_event:sync_notify ----->Module:handle_event/2
gen_event:call ----->Module:handle_call/2
------>Module:handle_info/2
gen_event:delete_handler ----->Module:terminate/2
gen_event:swap_handler
gen_event:swap_sup_handler ----->Module1:terminate/2
Module2:init/1
gen_event:which_handlers ----->-
gen_event:stop ----->Module:terminate/2
------>Module:code_change/3
因此,每个事件处理器都是回调模块,一个事件管理器有即可回调模块,并且可以动态的添加或删除。所以,gen_event比其它行为更能容错,如果一个事件处理器失败,但事件管理器并不会失败;如果删除一个事件处理器,并给与了一个错误的参数,但其他的事件处理器并不会受影响。sys模块可以用来调试一个事件管理器,一个gen_event就是通过它来处理system messages.通过回调函数的处理模块返回指定的'hibernate'值,让gen_event进程进入hibernate状态,但是这仅对对空闲事件比较长的服务器有用,然而这些特征应该被小心使用,因为意味着至少有两个GC,可能在忙碌的事件管理器处理事件时,你不能做你向处理的事情。特别注意:当有多个事件处理器被调用时,只要返回一个'hibernate'时整个事件管理器就进入了hibernate状态。如果事件管理器不存在或者被给与了错误的参数,访问所有方法都会失败。
2.函数
2.1 导出函数
start_link() -> Result
start_link(EventMgrName) -> ResultEventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}Result = {ok,Pid} | {error,{already_started,Pid}}监控树可以通过调用该方法创建一个事件管理器进程作为一颗监控树的一部分,并连接到监控树。如果EventMgrName={local,Name},事件管理器在本地被注册为Name通过register/2. 如果EventMgrName={global,GlobalName},事件管理器将在全局被注册为GlobalName通过global:register_name/2.如果没有指定名字,事件管理器将不会被注册.如果EventMgrName={via,Module,ViaName},事件管理器将通过Module被注册为ViaName.Module模块应该导出register_name/2, unregister_name/1, whereis_name/1 and send/2,它的原理与global相似。如果事件管理器被成功创建将返回{ok,Pid},如果指定名字的事件管理器已经存在将返回{error,{already_started,Pid}}
start() -> Result
start(EventMgrName) -> ResultEventMgrName = {local,Name} | {global,GlobalName} | {via,Module,ViaName}Result = {ok,Pid} | {error,{already_started,Pid}}创建一个独立与监控树的事件管理器。其参数与start_link/0,1一样。add_handler(EventMgrRef, Handler, Args) -> ResultEventMgr = Name | {Name,Node} | {global,GlobalName} | {via,Module,ViaName} | pid()Handler = Module | {Module,Id}Result = ok | {'EXIT',Reason} | term()向事件管理器EventMgrRef添加一个事件处理器,事件管理器将通过回调Module:init/1初始化事件处理器和它的内部状态。
EventMgrRef可以是:
- pid
- Name,本地注册的事件管理器名字
- {Name, Node},在本地另一个节点注册的事件管理器名字
- {global,GlobalName},全局注册的事件管理器名字
- {via,Module,ViaName},通过指定模块注册的事件管理器的名字
Handler是一个gen_event回调模块的名字或元组{Module, Id},元组{Module, Id}是一个特别的具有身份Id的gen_event回调模块,用来处理当事件处理器是同样一块gen_event回调模块的情况。Args是任意的数据类型,用来作为Module:init/1的参数。如果Module:init/1返回一个正确的值表明成功添加事件处理器,事件管理器的添加函数将返回ok.如果Module:init/1因为Reason失败或返回{error, Reason},这个事件处理器将被忽略并且该函数返回{'EXIT',Reason}或{error, Reason}.
- normal,如果事件处理器通过调用delete_handler/3被删除
- shutdown,如果事件处理器因为事件管理器终止而被移除
- {swapped,NewHandler,Pid},如果事件处理器被另一个事件处理器NewHandler替换通过swap_handler或swap_sup_handler/3.
- Term,事件处理器因为error而被删除,Trem取决于错误类型
sync_notify(EventMgrRef, Event) -> ok
call(EventMgrRef, Handler, Request, Timeout) -> Result
2.2 回调函数
3.实践
首先通过gen_event:start_link/1启动一个事件管理器:
- start_link()-> gen_event:start_link({local,?SERVER}).
同时终止管理器的方法为:
- stop()-> gen_event:stop(?SERVER).
当终止事件管理器时会调用事件处理器的Module:terminate(stop, ...),来依次关闭事件处理器。3.1 添加删除事件
添加事件处理器:
add_handler()->
gen_event:add_handler(?SERVER, myevent,[]),
gen_event:add_sup_handler(?SERVER, myerror,[]).将会调用Module:init/1初始化事件处理器。
删除事件处理器:
delete()->
gen_event:delete_handler(?SERVER,myerror,[]).将会调用Module:terminate/2来终止事件处理器。
3.2 事件消息
notity()->
gen_event:notify(?SERVER,liuwei). %%异步通知
notity_sync()->
gen_event:sync_notify(?SERVER,liuwei_sync). %%同步通知
handle_event(_Event,State)->
timer:sleep(2000),
io:format("Event:~p~n",[_Event]),
{ok,State}.
当返回{ok, NewState}时,事件管理器不变,并更新内部状态。当返回{swap_handler,Args1,NewState,Handler2,Args2}时,将替换老的事件管理器:
当返回remove_handler时,将删除事件处理器:
3.3 事件交换
swap()->
gen_event:swap_handler(?SERVER,{myevent,normal_swap},{myerror,[]}).首先会调用myevent:terminate(normal_swap, ...)来终止myevent事件处理器,并返回数据Term;然后调用myerror:init({[],Term})来启动myerror事件处理器。特别注意:新的事件处理器将被添加,即使指定的老的事件处理器未安装(Term=error);或Module1:terminate/2由于Reason失败(Term={'EXIT',Reason}).老的事件处理器将被删除,即使Module2:init/1失败。
3.4 监控事件
创建进程与事件管理器的监控连接:
%%启动服务器add1
start_link()->
gen_server:start({local,?SERVER},?MODULE,[],[]).
%%启动事件管理器myevent
start_link()->
{Flag,Pid}= gen_event:start_link({local,?SERVER}).
%%添加连接事件处理器
add_sup_handler()->
gen_server:call(?SERVER, add_sup_handler).
handle_call(add_sup_handler,_From,State)->
myevent:add_sup_handler(),
{reply, ok,State};
add_sup_handler()->
gen_event:add_sup_handler(?SERVER, myerror,["add_sup_handler:myerror"]).
%%删除连接的事件处理器
delete()->
gen_event:delete_handler(?SERVER,myerror,[]).
%%接受事件处理器退出信息
handle_info(_Info,State)->
io:format("add1::info:~p~n",[_Info]),
{noreply,State}.当事件处理器正常删除时(delete_handler/2):
当因为事件管理器终止时(stop/1):
当事件处理器因为替换而终止时(swap_handler/2):
当事件处理器因为error而终止时:
当调用进程终止时(Module:terminate({stop, Reason}, ...)来终止事件处理器):
gen_event:swap_sup_handler/3与上面一样,当老的事件处理器在替换成新的事件处理器时,将与调用进程间建立监控连接。
当我们在调用gen_event:swap_handler/3时,用老的事件处理器去替换新的事件处理器,当老的事件处理器与一个进程建立监控连接时,这时这个进程将与新的事件处理器建立监控连接:
4.总结
通过Gen_event我们可以实现通用的事件管理器,用于记录一些消息、警告、错误的信息。gen_event提供了标准的接口和回调函数来实现事件管理器与事件处理器。事件管理器可以动态的添加替换事件处理器,也可以动态的删除事件处理器。当一个事件到达时,所有安装的事件处理器都会处理该事件信息。事件管理器具有比其他行为模块的容错能力,当一个事件处理器失败时,并不会影响其他事件处理器。
Gen_event行为分析和实践的更多相关文章
- Log4j2分析与实践
当前网络上关于Log4j2的中文文章比较零散,这里整理了一下关于Log4j2比较全面的一些文章,供广大技术人员参考 Log4j2分析与实践-认识Log4j2 Log4j2分析与实践-架构 Log4j2 ...
- 苏宁基于Spark Streaming的实时日志分析系统实践 Spark Streaming 在数据平台日志解析功能的应用
https://mp.weixin.qq.com/s/KPTM02-ICt72_7ZdRZIHBA 苏宁基于Spark Streaming的实时日志分析系统实践 原创: AI+落地实践 AI前线 20 ...
- 《Linux内核分析》实践4
<Linux内核分析> 实践四--ELF文件格式分析 20135211李行之 一.概述 1.ELF全称Executable and Linkable Format,可执行连接格式,ELF格 ...
- 自定义View系列教程04--Draw源码分析及其实践
深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...
- Supervisor行为分析和实践
1.简介 Erlang要编写高容错性.稳定性的系统,supervisor就是用来解决这一问题的核心思想.通过建立一颗监控树,来组织进程之间的关系,通过确定重启策略.子进程说明书等参数信息来确定 ...
- Gen_server行为分析与实践
1.简介 Gen_server实现了通用服务器client_server原理,几个不同的客户端去分享服务端管理的资源(如图),gen_server提供标准的接口函数和包含追踪功能以及错误报告来实现通用 ...
- AWVS结果分析与实践-XSS
今天趁着老师接项目,做了一丢丢实践,以下是一点点感触. 都知道AWVS是神器,可是到我手里就是不灵.拿了它扫了一个URL,结果提示XSS漏洞,实践没反应,只好愉快地享受了过程.来看看. ...
- 基于redis的分布式锁的分析与实践
前言:在分布式环境中,我们经常使用锁来进行并发控制,锁可分为乐观锁和悲观锁,基于数据库版本戳的实现是乐观锁,基于redis或zookeeper的实现可认为是悲观锁了.乐观锁和悲观锁最根本的区别在于 ...
- FastText 分析与实践
一. 前言 自然语言处理(NLP)是机器学习,人工智能中的一个重要领域.文本表达是 NLP中的基础技术,文本分类则是 NLP 的重要应用.在 2016 年, Facebook Research 开源了 ...
随机推荐
- iOS 基础 第四天(0809)
0809 - 内存管理,只对oc对象生效. alloc.retain.release.retainCount 局部变量是放在栈里面的,oc对象是放在堆里面的.栈里面的内容系统自动回收,而堆里面的内容还 ...
- 【BZOJ 2440】[中山市选2011]完全平方数
Description 小 X 自幼就很喜欢数.但奇怪的是,他十分讨厌完全平方数.他觉得这些数看起来很令人难受.由此,他也讨厌所有是完全平方数的正整数倍的数.然而这丝毫不影响他对其他数的热爱. 这天是 ...
- 微软职位内部推荐-Principal Dev Manager for Windows Phone Shell
微软近期Open的职位: Location: China, BeijingDivision: Operations System Group Engineering Group OverviewOSG ...
- 基于SuperSocket实现的WebSocket(前端)
本文内容是搭配后端使用的,没看过WebSocket后端实现的童鞋们戳这里 咳咳,其实前端实现相对就容易很多了,因为我们有JavaScript WebSocket Api,它看上来大致是这样的: var ...
- Qt编译postgreSQL驱动
安装postgreSQL,安装目录下的lib和bin添加到path 打开Qt安装目录,找到src\plugins\sqldrivers\psql编辑psql.pro,添加INCLUDEPATH += ...
- bnuoj 20832 Calculating Yuan Fen(暴力模拟)
http://www.bnuoj.com/bnuoj/problem_show.php?pid=20832 [题意]: 给你一串字符串,求一个ST(0<ST<=10000),对字符串中字符 ...
- poj 3522 Slim Span (最小生成树kruskal)
http://poj.org/problem?id=3522 Slim Span Time Limit: 5000MS Memory Limit: 65536K Total Submissions ...
- MyISAM读写并发优化
MyISAM在读操作占主导的情况下是很高效的.可一旦出现大量的读写并发,同InnoDB相比,MyISAM的效率就会直线下降,而 且,MyISAM和InnoDB的数据存储方式也有显著不同:通常,在MyI ...
- asp.net MVC日志插件Log4Net学习笔记一:保存日志到本地
log4net(Log For Net)是Apache开源的应用于.Net框架的日志记录工具,详细信息参见Apache网站.它是针对Java的log4j(Log For Java的)姊妹工具.用过lo ...
- spoj 368
额 最小生成树 ........ #include <cstdio> #include <cstring> #include <algorithm> using ...