反向Ajax,第5部分:事件驅動的Web開發

作者: Mathieu Carbou  來源: 譯言  發布時間: 2011-10-19 12:05  閱讀: 4337 次  推薦: 0   原文鏈接   [收藏]  

  英文原文:Reverse Ajax, Part 5: Event-driven web development

  前言

  這一文章系列展示了如何使用反向Ajax(Reverse Ajax)技術開發事件驅動的web應用,第1部分內容介紹了反向Ajax、輪詢(polling)、流(streaming)、Comet和長輪詢(long polling);第2部分內容說明了如何使用WebSocket來實現反向Ajax,并討論了使用Comet和WebSocket的web服務器的局限性;第3部分內容說明了如果需要支持多種服務器,或是給用戶提供部署在他們自己的服務器上的獨立的web應用的話,那么實現自己的Comet或是WebSocket通信系統會有哪些難點,該部分內容還討論了Socket.IO;第4部分內容談到了Atmosphere和CometD——最知名的用于Java技術服務器的開源反向Ajax庫。

  到目前為止你已經了解了創建通過事件來通信的組件,在本系列的最后一部分內容中,我們把事件驅動開發的原則應用到實踐中,構建一個示例性的事件驅動web應用。

  你可以下載本文中使用的源代碼。

  前提條件

  理想情況下,要充分體會本文的話,你應該對JavaScrpit和Java有一定的了解,并且要有一些web開發經驗。若要運行本文中的例子,你還需要最新版本的Maven和JDK(參見參考資料)。

  術語

  你可能對事件驅動架構(event-driven architecture,EDA)、EventBus系統、消息系統、復雜事件處理(complex event processing,CEP)和信道(channel)這些說辭并不陌生,這些術語和概念已出現多年。隨著技術的成熟,你可能會更頻繁地聽到這類說法。本節內容為這些概念提供一些簡短的解釋。

  事件(event)

  在系統中會發生的一些事情的出現,事件通常具有屬性,比如說出現的時間(時間戳)、來源或位置(我們點擊的組件),以及一些描述事件的數據。根據系統的不同,事件還可以有其他的一些屬性選擇。

  事件驅動架構(Event-driven architecture,EDA)

  也稱作基于事件的編程,這是一種架構設計,在這種設計中,應用由通過發送和接收事件來通信和執行的組件構成。Swing的圖形化用戶界面(GUI)就是一個EDA例子,每個Swing組件都可以監聽事件、對事件作出反應、發送其他事件等。EDA由幾個部分組成:事件生產者、事件消費者、事件和處理軟件。

  1. 事件生產者(event producer)——該組件發出事件。在本文的例子中,表單的提交按鈕就是一個事件生產者。

  2. 事件消費者(event consumer)——監聽特定事件的組件。例如,例子中的表單提交這種情況,瀏覽器監聽表單的提交按鈕上的點擊操作,把表單數據發送給服務器。

  3. 事件處理軟件(event-processing software)——這是系統的核心,事件生產者發布事件,事件消費者注冊自身以接收事件。根據軟件的不同,處理過程可以很簡單(只是把接收到的事件轉發給消費者),或很復雜(CEP)。有了CEP,軟件就可以支持各種各樣的處理方式,比如說事件的匯集、過濾和轉換等。

  Esper就是這樣的一個軟件例子。事件處理軟件不僅可以表現成一個獨立的運行應用,其還可以是整合到你的應用中的庫。

  消息系統(messaging system)

  一種事件驅動應用類型,在這種應用中,事件生產者把消息發布到信道中,事件消費者則通過信道進行訂閱。事件生產者和消費者彼此之間沒有鏈接,是完全獨立開來的。在這種類型的事件驅動應用中,通常用到的術語是消息(message)而不是事件(event)。

  信道(channel)

  消息系統中分類事件的一種方式。其代表了事件生產者希望事件發送到的目的地。例如,在一個聊天室應用中,某個信道可能會是 /chatapplication/chatrooms/asdrt678,該信道標識了一個事件生產者可以發送消息的特定的聊天室,圖形化的組件應該要訂閱該信道,目的是顯示最新到達的消息。

  某些消息系統提供了兩種類型的信道:隊列(queue)和主題(topic)。

  1. 隊列(queue)——當某條消息被發送到隊列中時,只有一個事件消費者拿到并處理該條消息,其他消費者不會看到它。隊列可被持久化,以保證交付。最好的隊列例子是郵遞請求,某個web應用在用戶注冊時發布一條消息到隊列 /myapp/mail/user-registration中,可能有多個郵件應用訂閱了這一隊列,如果沒有的話,消息也不會丟失。

  2. 主題(topic)——當某條消息發送到某個主題上時,每個訂閱者都可以接收到它,主題通常是沒有持久化的。一個例子是監控軟件的一個主題/event/system/cpu/usage,生產者定期往其中發送CPU的使用情況;另一方面,這一主題可能會沒有或是有多個訂閱者,這取決于他們的興趣所在。

  發布/訂閱(publish/subscribe)

  事件驅動的解決方案實現了發布/訂閱模式。事件生產者在處理軟件中發布事件,事件消費者通過訂閱來接收它們。事件消費者訂閱的方式依賴于軟件。在消息應用中,它們訂閱信道(比如說,還可以有選擇地把過濾規則應用在事件類型上)。使用諸如Esper一類的CEP,可通過類SQL的請求來定義你所感興趣的事件,完成訂閱操作。

  為什么使用事件驅動的解決方案

  在一個傳統的通信方案中,如果系統A需要來自系統B的信息,就會發送一個請求給B。系統B會處理該請求,系統A則會停在那里等待響應。在處理完成時,響應會送回給系統A。在這一同步的通信模式中,資源的消耗是低效的,因為在等待響應時浪費掉了處理時間。

  在異步模式中,系統A會從系統B中訂閱它響應的信息。然后系統A可以選擇性地給系統B發送通知,并立刻返回,系統A可以繼續處理其他事情,這一步驟是可選的。通常情況下,在事件驅動的應用中,你不需要請求其他系統發送事件,因為你不知道它們是誰。當系統B發布響應時,系統A會立刻接收到。

  事件驅動架構的一個優點是其允許更好的伸縮性。可伸縮性是系統在滿足其目標的同時適應需求、容量或是強度變化的能力。通過消除暫停時間,事件驅動的架構通常有著更好的表現,以及有更高的處理效率。

  另一個優點表現在應用的開發和維護方面。使用事件驅動的解決方案,應用的每個組件都可以是完全獨立和解耦的。

  事件驅動的解決方案允許有更好的反應時間,因為通信有著更低的延遲。

  把事件驅動的解決方案應用在web上

  web框架過去依賴于傳統的請求-響應模式,這導致了頁面的刷新。隨著Ajax、反向Ajax以及諸如CometD和Atmosphere一類的功能強大的框架的出現,現在把事件驅動架構的概念應用到web上來獲取解耦、可伸縮性和反應性的好處已經不是什么難事了。

  客戶端

  事件驅動架構可應用在GUI開發的客戶端。與創建一個傳統的web頁面不同,你可以把一個單獨的web頁面當作容器使用。每個組件(頁面的每個組成部分)都可以是獨立的,你可以在web上放一個Java Swing GUI,就像包含了小工具(gadget)的Google頁面那樣。

  你需要一個事件總線(event bus),例如,你需要開發一個JavaScript事件總線,其允許每個頁面組件從信道訂閱或是在信道中發布。事件也可以是異步的,在兩個或是多個事件到達后才觸發行為。事件總線可以用于頁面中的局部事件,但你也可以通過使用CometD或是Socket.IO來以插件的方式支持遠端事件。

  服務器端

  在服務器端,你需要設置一個反向Ajax框架來支持事件驅動的框架。在本系列前幾部分的考察中,發現只有CometD有著事件驅動的方法。對于其他框架來說,你需要增加自定義的支持,這不是什么大問題。你還可以加入第三方的消息系統,比如說JMS(例如Apache ActiveMQ)或是像Esper那樣的CEP。一個更簡單的解決方案是Redis,其支持基本的發布/訂閱。

  這一文章系列談論的是事件驅動的web和反向Ajax,因此我們重點關注客戶端,不會去設置一個復雜的消息系統。

  事件驅動web的例子

  本文將要創建的例子是一個聊天室web應用,該應用使用一個用戶面板來列出連接的用戶。你的用戶名是加粗顯示的,活動用戶(20秒鐘后還處活躍狀態的那些)是綠色顯示的,20秒鐘后處于非活動狀態的那些是橙色顯示的。如果有用戶連接或是斷開連接,列表就會刷新。

  出于安全目的,web.xml文件中配置了兩分鐘的會話超時,非活動狀態兩分鐘后,就會彈出一個窗口,你會被重定向到登錄頁面。

  只要你不再處于會話中或是還未連接,就會被重定向到登錄頁面。登錄頁面要求輸入用戶名并會查看是否可讓你登錄到聊天室中。

  一旦登錄成功,你就可以在聊天室中給所有用戶發送消息。一個控制臺也會顯示出來,記錄所有收到的事件。

  該web應用是基于事件的,有了上述的信息,你可以很容易地定義幾個事件:

  1. 用戶連接

  2. 用戶斷開連接

  3. 會話過期

  4. 接收到聊天消息

  5. 如果沒有登錄的話,安全過濾器阻攔請求

  6. 用戶變成非活動的

  7. 用戶變成活動的

  8. 所有其他與UI協調相關的事件

  某些事件只局部于web應用,由局部總線來識別,如清單1所示:

  清單1. 總線設置

bus = { 
    local: new EventBus({
        name: 'EventBus Local'
    }), 
    remote: EventBus.cometd({
        name: 'EventBus Remote',
        logLevel: 'warn',
        url: document.location.href.substring(0,
            document.location.href.length -
            document.location.pathname.length) + '/async',
        onConnect: function() {
            bus.local.topic('/event/bus/remote/connected').publish();
        },
        onDisconnect: function() {
            bus.local.topic('/event/bus/remote/disconnected').publish();
        }
    }) 
};

  其他事件是遠端的,這意味著它們需要一個反向Ajax系統,比如說CometD來在所有客戶端中發布它們。圖1展示了該示例應用。

  圖1. 示例應用

  你可以下載這一示例應用,許多類都是安全通道類,或是會話和用戶管理通道類。本文給出了代碼最重要的部分,不過建議你下載并運行該應用例子來更加深入地了解它的運作方式。

  該web應用有不同的組件構成:聊天室、用戶列表和控制臺。每個都很獨立,可以拿掉而不會影響到其他部分。

  為了以局部的和遠端的方式來設置這一事件驅動的系統,該例子使用了Ovea的EvenBus系統。其提供了一個局部的事件總線,一個活動遠端事件的ComeD橋接,以及一種協調事件的方式(在幾個事件完成之后觸發行為)。當然,你可以選擇使用另一個不同的系統。該例子使用了JavaScript來進行設置,如清單1所示。

  一旦總線就位了之后,應用和組件就是基于事件的了。在本例子中,設置的是IDLE檢測系統,如清單2所示。                

  清單2. IDLE檢測系統

bus.local.topic('/event/dom/loaded').subscribe(function() {
    $.idleTimer(20000);
    $(document).bind('idle.idleTimer', function() {
        bus.local.topic('/event/idle').publish('inactive');
    });
    $(document).bind('active.idleTimer', function() {
        bus.local.topic('/event/idle').publish('active');
    });
})

  有了清單2中的代碼,IDLE系統就會在檢測到活動時發送事件。這一代碼可用在任何需要IDLE系統的應用中。在該例子中,你需要在用戶活動的遠端事件中轉化一下該代碼。其也可用JavaScript來實現,如清單3所示。

  清單3. 用戶活動管理

bus.local.topic('/event/idle').subscribe(function(status) {
    bus.remote.topic('/event/user/status/changed').publish({
        status: status == 'active' ? 'online' : 'away'
    });
}); 
bus.remote.topic('/event/user/status/changed').subscribe(function(evt) {
    if(evt.user != me.name) {
        $('#users li').filter(function() {
            return evt.user == $(this).data('user').name;
        }).removeClass('online')
          .removeClass('away')
          .addClass(evt.status);
    }
});

  首個訂閱接收來自IDLE系統的事件,然后把用戶狀況發送給服務器端。其他的訂閱接收來自服務器端的用戶狀況事件。因此,只要用戶的狀況發生改變,用戶列表中的用戶的顏色就會變成綠色或是橙色。

  當用戶連接或是斷開連接時,就會發送一個事件,如清單4所示:

  清單4. 用戶列表管理

bus.remote.topic('/event/user/connected').subscribe(function(user) {
    $('#users ul').append(row(user));
}); 
bus.remote.topic('/event/user/disconnected').subscribe(function(evt) {
    $('#users li').filter(function() {
        return evt.user == $(this).data('user').name;
    }).remove();
});

  應用的代碼簡單、解耦且是獨立的,通過重用Ovea的許多技術,你可以快速地創建事件驅動的web應用。不過,因為可以使用其他的系統來代替它,因此這并不是必需的。該例子只花了一天的開發時間,其中一半的代碼都是管道代碼,包括:

  1. Maven:構建工程

  2. 安全功能(登錄、退出、會話超時)

  3. 使用了Jersey的REST服務

  結束語 

  該文章系列展示了如何構建響應式的以及是可伸縮的應用,這些應用能夠提供很好的用戶體驗。事件驅動的web應用還是一個相當新的概念,某些WEB框架在內部用到了這些概念。不過本文展示的是不需要web框架來構建的這樣的應用,對于要分離Java開發者和web設計者之間的職責來說,使用好的、專業的庫就已是綽綽有余的了。關于如何把事件驅動的開發變成你的日常工作的一部分,我希望你已經有了一個較為深入的理解。它很容易讓人沉迷于其中,一旦經過嘗試,你就不想再回退到傳統的框架上了。

  代碼下載

  reverse_ajaxpt5_source.zip

0
0
 
 
 

文章列表

全站熱搜
創作者介紹
創作者 大師兄 的頭像
大師兄

IT工程師數位筆記本

大師兄 發表在 痞客邦 留言(0) 人氣()