4.Gen_Event行为

本章应和 gen_event(3) 相结合阅读,它包含了所有接口函数和回调函数的详细说明。

事件处理原理

在OTP中, 事件管理器 (事件管理器)是一个命名对象,可以给其发送事件。一个 事件 (event)可以是诸如一个错误、一个警报或者是某种应被记录的信息。

在事件管理器中,可以安装零个、一个或者多个 事件处理器 (事件处理器)。当事件管理器被通知有一个事件时,所有安装了的事件处理器都会来处理该事件。例如,一个处理错误的事件管理器可以默认安装了一个将错误消息打印到终端的处理器。如果在某个特定期间,错误消息也要被保存到一个文件中,那么用户可以添加另外一个做这个事情的事件处理器。当不再需要记录到文件时,该事件处理器就会被删除。

一个事件管理器实现为一个进程,而每个事件处理器则实现为一个回调模块。

事件管理器本质上是在维护一个 {Module, State} 对的列表,其中每个 Module 是一个事件处理器, State 是事件处理器的内部状态。

例子

将错误消息输出到终端的事件处理器的回调模块可以这样写:

-module(terminal_logger).
-behaviour(gen_event).

-export([init/1, handle_event/2, terminate/2]).

init(_Args) ->
    {ok, []}.

handle_event(ErrorMsg, State) ->
    io:format("***Error*** ~p~n", [ErrorMsg]),
    {ok, State}.

terminate(_Args, _State) ->
    ok.

将错误消息写入到文件的事件处理器的回调模块可以是:

-module(file_logger).
-behaviour(gen_event).

-export([init/1, handle_event/2, terminate/2]).

init(File) ->
    {ok, Fd} = file:open(File, read),
    {ok, Fd}.

handle_event(ErrorMsg, Fd) ->
    io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),
    {ok, Fd}.

terminate(_Args, Fd) ->
    file:close(Fd).

这些代码将在下一节中解释。

启动一个事件管理器

要启动上面例子中描述的处理错误的事件管理器,可以调用如下方法:

gen_event:start_link({local, error_man})

这个方法生产一个新的事件管理器进程并连接它。

参数 {local, error_man} 指定了名字。在这里,事件管理器将在本地注册为 error_man 。

如果名称被忽略,那么就不会注册事件管理器。这时就必须使用它的pid。名称也可以以 {global, Name} 的形式给出,这种情况下则会使用 global:register_name/2 来注册事件管理器。

如果事件管理器是监督树的一部分——即由一个督程启动的——那么必须使用 gen_event:start_link 启动。还有另外一个函数 gen_event:start 用于启动一个独立的事件管理器,即,不属于任何监督树的事件管理器。

添加事件处理器

以下例子使用了一个命令行演示了如何启动事件管理器并添加一个事件处理器:

1> gen_event:start({local, error_man}).
{ok,<0.31.0>}
2> gen_event:add_handler(error_man, terminal_logger, []).
ok

这函数给注册为 error_man 的事件管理器发送了一个消息,告诉它要添加事件处理器 terminal_logger 。这个事件管理器会调用回调函数 terminal_logger:init([]) ,其中参数[]是传给 add_handler 的第三个参数。 init 要返回 {ok, State} ,其中 State 是事件处理器的内部状态。

init(_Args) ->
    {ok, []}.

这里, init 无须任何输入数据并忽略了它的参数。同样,对于 terminal_logger 来说内部状态也是用不到的。对于 file_logger 来说,内部状态用于保存打开的文件描述符。

init(_Args) ->
    {ok, Fd} = file:open(File, read),
    {ok, Fd}.

事件通知

3> gen_event:notify(error_man, no_reply).
***Error*** no_reply
ok

error_man 是事件管理器的名字, no_reply 是事件。

事件被作为一个消息发送给事件管理器。当收到了这个事件以后,事件管理器为每个安装了的事件处理器按照他们添加的顺序调用 handle_event(Event, State) 。函数要返回一个元组 {ok, State1} ,其中 State1 是事件处理器的状态的新值。

在 terminal_logger 中:

handle_event(ErrorMsg, State) ->
    io:format("***Error*** ~p~n", [ErrorMsg]),
    {ok, State}.
handle_event(ErrorMsg, Fd) ->
    io:format(Fd, "***Error*** ~p~n", [ErrorMsg]),
    {ok, Fd}.

删除事件处理器

4> gen_event:delete_handler(error_man, terminal_logger, []).
ok

这函数给注册为 error_man 的事件管理器发送了一个消息,告诉它要删除处理器 terminal_logger 。事件管理器会调用回调函数 terminal_logger:terminate([]) ,其中参数[]是传给 delete_handler 的第三个参数。 terminate 应该和 init 相反,进行必要的清理工作。它的返回值会被忽略。

对于 terminal_logger ,无须任何清理:

terminate(_Args, _State) ->
    ok.

对于 file_logger ,在 init 中打开的文件描述符需要被关闭:

terminate(_Args, Fd) ->
    file:close(Fd).

停止

当停止事件管理器时,会通过调用 terminate/2 让每个事件处理器都有机会进行清除工作,与删除处理器的方法一样。

在在监督树中

如果事件管理器是监督树的一部分,那无须停止函数。它的督程会自动终止它。具体如何进行是通过督程中的关闭策略集来定义。

独立的事件管理器

要停止一个事件管理器也可以调用:

> gen_event:stop(error_man).
ok
时间: 2024-10-29 03:53:44

4.Gen_Event行为的相关文章

gen_event学习

gen_event 是通用的事件处理行为. 描述: 一个实现事件处理功能的行为模块.OTP事件处理模块包括一个通用的事件管理,负责处理可以动态添加.删除的任意数量的事件.用这个模块实现的事件管理器会有一组标准的功能接口,包括跟踪和错误报告功能.它也将融入OTP 监督树中,请参考OTP设计原则了解更多信息. 每个事件处理以回调方式来实现,回调需要导出一组预先设计好的函数,行为函数和回调函数的关系如下: gen_event module Callback module ---------------

Gen_event行为分析和实践

1.简介 Gen_event实现了通用事件处理,通过其提供的标准接口方法以及回调函数,在OTP里面的事件处理模块是由一块通用的事件管理器和任意数量的事件处理器,并且这些事件处理器可以动态的添加和删除.一个事件可以用来记录error,alarm,info, warning等信息.一个事件管理器可以安装0,1,N个事件处理器,当一个事件管理器接受到一个事件的通知时,这个事件将会被所有的已安装的事件处理器处理(如图). 事件管理器实质上是{Module, State}组成的列表,每个Module是一个

erlang四大behaviour之三-gen_event

来源:http://www.cnblogs.com/puputu/articles/1689623.html 1. 事件处理规则 在OTP中,事件管理器是一个事件可以发送到的命名对象,一个事件可以是一个错误.一个警告.或者一些要写入日志的信息 在事件管理器中,有0个.一个或者多个事件处理器被安装,当事件管理器被一个事件通知时,这个事件将被安装在事件管理器中的事件处理器处理, 事件管理器用一个进程实现,事件处理器用回调模块实现.事件管理器本质上维护一个{Module, State}列表,每一个Mo

【转】erlang四种监控策略one_for_one、one_for_all、simple_one_for_one、rest_for_one

Supervisor Behaviour是一个用来实现一个supervisor进程来监控其他子进程的模块 子进程可以是另一个supervisor,也可以是一个worker进程 worker进程一般使用gen_event,gen_fsm或gen_server behaviour来实现 一个使用该模块来实现的supervisor有一个接口方法的标准集,包括跟踪和错误报告的功能 supervisor用来构建一个分层进程结构,称为supervision tree,这是组织一个容错系统的好方式 1,Sup

Erlang 104 OTP - incomplete

笔记系列 Erlang环境和顺序编程Erlang并发编程Erlang分布式编程YawsErlang/OTP 日期              变更说明 2014-12-21 A Outline, 1 Agenda 0 Scope 围绕OTP设计原则,分别记录行为模式.监督树概念.应用.发布和部署,以及[3]中一个产品级缓存解决方案. 1 OTP Design Principles Erlang Doc: OTP Design Principles User's Guide OTP设计原则阐述的是如

OTP的supervisor tree如何保证子进程一定随父进程的退出而退出

利用OTP行为包构建的应用之所以可靠,是因为我们按照OTP的设计模式,将所有进程组织成了一棵可靠的supervisor tree.每一个supervisor监控其子进程,并在其子进程出错时按照重启策略进行相应的处理. 但是,你是否考虑过,如果supervisor意外终止,其子进程会怎样?当然,直觉告诉我们连监控进程的没有了,所有的子进程应全部终止.但是,你在代码中是否真正考虑过这种情况?你的gen_server可否写过如下代码? handle_info({'EXIT', Parent, Reas

Erlang tool -- lager overload protection

log 这个事, 说大不大说小又不小. 大点的, 可以用scribe flume 这样的系统去做, 小点的, 也就打印一个调试信息而已. 在Erlang 中, log 这事情确实比较伤, error_logger 是个单点, io:format 容易导致节点崩溃. 在开源社区, lager 算是使用比较广泛的一个, 然而, 同样不能完全避免单点的问题. 因为在lager 中, lager_event 作为 core, 是单个进程存在的. 1 dispatch_log(Severity, Meta

日志系统。

一.SASL SASL全称System Architecture Support Libraries,提供如下几个服务: alarm_handler overload rb release_handler systools SASL带有error_logger的事件处理句柄用于格式化SASL错误和crash报告,这些句柄如下: sasl_report_tty_h   sasl_report_file_h   error_logger_mf_h 细节参考OTP文档可知. SASL的默认event

当我参加培训的时候,我在学什么?

https://zhuanlan.zhihu.com/p/26114277 ***************************************** 在旧金山举行的 erlang/elixir 2017 大会上周结束.这次,我并未参加 -- 权衡再三,我选择了这周的 complete OTP 培训,毕竟大会的视频 youtube 上找得见,可以慢慢补,培训错过了就没了. 参加一次技术培训,代价往往不菲,像这样一个四天的培训,价格是两千多刀,你很难说出它有多值 -- 培训的主题有一半都是