事件驅動的應用開發模型

從目前看,大量數據的流動仍然主要分佈在局域網的分佈式系統中,該類系統的大流量、實時性的特點要求系統具有實時響應、交互動作異步非耦合、高可用性、高可得到性等特徵。而因爲系統主要侷限在局域網內運行,因而在系統的構建上應用要具有靈活多樣可靠穩定的性能。事實上,良好的局域網應用是聯入廣域網的前提。

在該類分佈式系統中,引導數據流動和分佈式動作的往往是事件的作用,或者稱之爲消息。事件是激活和驅動分佈式系統的直接原因,也是進一步構建分佈式對象管理的基礎。利用事件驅動開發模型,可以快速構建分佈式系統應用,提高分佈式應用和整個分佈式系統的運行效率。

事件驅動的開發模型

首先,在分佈式系統中,事件是異步非耦合的,這是系統實時性的要求。因爲在分佈式系統中,往往各個關鍵應用既是服務器應用,也是客戶應用,相互之間從功能上看是對等關係,如果在同步情況下,一個應用驅動了另一個應用的事件,這個應用則必須堵塞,以等待事件執行的返回狀態,這樣這個應用在這段時間內則不能處理實時事件。而各個應用之間相互不完全依賴的情況也決定了分佈式系統中事件的兩端必須爲非耦合。

事件驅動的開發模型包括如下幾個部分:

  • 事件定義庫:該庫定義了應用所有認知的事件描述,基本信息包括事件標識,事件特徵,事件處理例程,事件所屬軟件包等。
  • 事件檢測模塊:該模塊負責處理事件的收集和分發,當接收事件後,分析事件的類型、特徵,然後作出相應的處理,該模塊是開發模型的核心。
  • 事件執行模塊:負責獲知事件入口,解碼事件攜帶數據,正確執行相應事件。事件執行模塊可以支持事件的持久性,持久性事件的接收方如果中間停止,而在再次啓動後仍可以接收事件。
  • 事件信道:是分佈式應用之間事件流動的通道。事件信道是單向的,適應分佈式應用的多樣性要求,事件信道存在多種類型,主要有: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較快的開發出相應的軟件包。

結束語

該開發模型事件驅動的細節都封裝在內部,總體結構清晰明瞭,只留了一些註冊、驅動事件的接口給應用開發程序員。有了基於事件驅動軟件包後,可以繼續開發分佈式對象管理,增強分佈式系統的構建能力。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章