现在的位置: 首页 > 综合 > 正文

事件驱动的应用开发模型

2014年02月10日 ⁄ 综合 ⁄ 共 2998字 ⁄ 字号 评论关闭

从目前看,大量数据的流动仍然主要分布在局域网的分布式系统中,该类系统的大流量、实时性的特点要求系统具有实时响应、交互动作异步非耦合、高可用性、高可得到性等特征。而因为系统主要局限在局域网内运行,因而在系统的构建上应用要具有灵活多样可靠稳定的性能。事实上,良好的局域网应用是联入广域网的前提。

在该类分布式系统中,引导数据流动和分布式动作的往往是事件的作用,或者称之为消息。事件是激活和驱动分布式系统的直接原因,也是进一步构建分布式对象管理的基础。利用事件驱动开发模型,可以快速构建分布式系统应用,提高分布式应用和整个分布式系统的运行效率。

事件驱动的开发模型

首先,在分布式系统中,事件是异步非耦合的,这是系统实时性的要求。因为在分布式系统中,往往各个关键应用既是服务器应用,也是客户应用,相互之间从功能上看是对等关系,如果在同步情况下,一个应用驱动了另一个应用的事件,这个应用则必须堵塞,以等待事件执行的返回状态,这样这个应用在这段时间内则不能处理实时事件。而各个应用之间相互不完全依赖的情况也决定了分布式系统中事件的两端必须为非耦合。

事件驱动的开发模型包括如下几个部分:

  • 事件定义库:该库定义了应用所有认知的事件描述,基本信息包括事件标识,事件特征,事件处理例程,事件所属软件包等。
  • 事件检测模块:该模块负责处理事件的收集和分发,当接收事件后,分析事件的类型、特征,然后作出相应的处理,该模块是开发模型的核心。
  • 事件执行模块:负责获知事件入口,解码事件携带数据,正确执行相应事件。事件执行模块可以支持事件的持久性,持久性事件的接收方如果中间停止,而在再次启动后仍可以接收事件。
  • 事件信道:是分布式应用之间事件流动的通道。事件信道是单向的,适应分布式应用的多样性要求,事件信道存在多种类型,主要有:1)永久可靠的信道。应用之间具有频繁的事件往来或固定的周期性的关键事件要采用这类信道;2)一次性可靠信道。事件的收发具有偶然性,不经常性,但是事件不可丢失;3)广播数据报不可靠信道。信道上的事件是一对多的关系,可以容忍偶然的数据报丢失或失序,如某一些关键应用的指示存在的心跳事件可采用该类信道;4)单播数据报信道;5)数据报可靠信道。这类信道主要适应于一对多的可靠事件,该信道在不可靠数据报基础上增加了数据报序列号和事件重发请求,以便形成可靠的事件通道。事件通道为分布式应用提高运行效率提供了基础设施。
  • 事件输入源:是驱动事件的源应用。包括了事件数据的编码和封装,编码是为了使得事件可以在不同的系统平台上流动,封装的事件数据中,头部信息固定的为公共可以识别的信息。
  • 最后要提供事件驱动应用API:包括了事件的注册API,以及实现以上各功能的各类API。API要求简单明了,为应用程序员快速构建分布式应用提供支持。

Linux系统上事件驱动的开发框架

目前,Linux操作系统逐渐为人们所接受,在Linux上的分布式系统也逐渐获得了更广泛的应用。本文在这里描绘了在Linux系统上基于以上事件驱动模型的简化开发框架。基于这个框架,还可以在Linux上开发出实用、可靠、稳定的事件驱动软件包。

2.1 事件定义库
事件定义库记录了应用所关心的所有事件信息,是事件测试模块的扫描数据区。定义库要求查询方便快速,定义库可以自由扩展。例如可以用以下的结构实现:

   typedef struct {
        // 指向下一个事件信息的指针
        void *ptr;
        // 事件ID
    int event_id;
    // 事件属性,指示事件是否活动等。
        int event_propertiy;
        // 定义事件的数据内容
        struct event_info info;
        // 事件处理例程,包括了数据信息和信道信息
        int (int *)(*event_process*)(void *data_buff, void *channel_info);
    } event_table
    struct event_db {
        // 有效的事件数量
        int event_num;
        // 事件列表
        event_table *event;
    }

2.2 事件检测模块
事件的检测是通过对一系列感兴趣的文件描述符的检测来完成的。感兴趣的文件描述符包括在文件描述符集内,一个感兴趣的文件描述符对应于一类事件。当我们检测到一个文件描述符可读时,就知道应用内已经驱动了一个事件。通过扫描事件定义库可以找到相应的事件,然后就可以提交给事件执行模块。

可以用如下代码实现事件检测模块:

   int rts;
    fd_set fdset;
    ......
    rts = select(FD_SETSIZE,&fdset,NULL,NULL,NULL,0);
    if(rts > 0 ) {
        if(rts == ......) { /* 如果检测到I/O描述符为某一类型的描述符,则*/ 
            ......      /* 将数据提交给事件执行模块 */
        }
        else if(rts == ......) {
            ......
        }
    }

2.3 事件注册模块
事件要注册后,应用才可以辨识并驱动该事件。事件的注册过程实际上是定义事件信息,将信息写入事件定义库的过程。

2.4 事件信道
事件信道不仅定义了事件流动的基础设施,也定义了相应的事件I/O描述符。例如在一个应用内部的驱动事件,事件信道的实现可以采用命名管道,返回的描述符用于检测内部的驱动事件。可以用以下代码实现:

   int io_inner
    mkfifo(fifo_filename,mode) // 创建一个FIFO文件
    io_inner = open(fifo_filename, mode); // 获得该文件描述符

应用之间的驱动事件的信道要用到socket描述符。应用之间建立了socket链路后即建立了事件流动的信道,返回的socket描述符用于检测应用之间的驱动事件。各种类型的信道均可加入检测的I/O描述符集。注意,对应于服务器端,执行如下代码:

int sock = socket(......),

返回的描述符可以直接加入检测描述符集,而当在检测到客户连接后,返回新的描述符也要加入检测描述符集,因为这个描述符才是检测驱动事件的描述符。如将创建的sock加入检测描述符集:

FD_SET(sock, &fdset);

2.5 事件执行模块
当有驱动事件时,事件检测模块将调用事件的执行模块,事件的执行模块则接收事件数据信息,同时解析事件驱动源信息和信道信息。

对应应用内部驱动事件,当检测到命名管道描述符可读时,则检测事件定义库,当事件处于活动时,则接收定义库内缓冲区的数据执行该事件例程。对应于应用之间的驱动事件,则当检测到socket描述符可读时,启动如下代码:

rts = recv(sock, (void *) buff, ......)

接收到事件数据后,检测头部信息,取出要驱动的事件ID,连通接收的通信数据提交给事件处理例程。

2.6 事件的驱动
对应应用内部事件,一方面通过命名管道通知检测模块,另一方面,要把事件的数据写入定义库,并设置事件处于活动状态。而应用之间的驱动事件,对数据进行封装和编码后,在底层要用如下socket函数发送:

rts = send(sock, (void *) buff, ......);

安装如上的驱动事件开发框架,可以在Linux较快的开发出相应的软件包。

结束语

该开发模型事件驱动的细节都封装在内部,总体结构清晰明了,只留了一些注册、驱动事件的接口给应用开发程序员。有了基于事件驱动软件包后,可以继续开发分布式对象管理,增强分布式系统的构建能力。

抱歉!评论已关闭.