知識
不管是網站,軟件還是小程序,都要直接或間接能為您產生價值,我們在追求其視覺表現(xiàn)的同時,更側重于功能的便捷,營銷的便利,運營的高效,讓網站成為營銷工具,讓軟件能切實提升企業(yè)內部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!
反向Ajax,第5部分:事件驅動的Web開發(fā) (轉)
發(fā)表時間:2012-7-2
發(fā)布人:葵宇科技
瀏覽次數(shù):48
這一文章系列探討了如何使用反向Ajax(Reverse Ajax)技術開發(fā)事件驅動的web應用,第1部分內容介紹了實現(xiàn)反向Ajax通信的不同方式;第2部分內容說明了如何使用WebSocket來實現(xiàn)反向Ajax,并討論了使用Comet和WebSocket的web服務器的局限性;第3部分內容探討了實現(xiàn)自己的Comet或是WebSocket通信系統(tǒng)的難點,并介紹了Socket.IO;第四部分內容談到了Atmosphere和CometD。在本系列的最后一部分內容中,我們來了解事件驅動的開發(fā),使用附帶的源代碼來構建一個示例性的事件驅動web應用
前言
這一文章系列展示了如何使用反向Ajax(Reverse Ajax)技術開發(fā)事件驅動的web應用,第1部分內容介紹了反向Ajax、輪詢(polling)、流(streaming)、Comet和長輪詢(long polling);第2部分內容說明了如何使用WebSocket來實現(xiàn)反向Ajax,并討論了使用Comet和WebSocket的web服務器的局限性;第3部分內容說明了如果需要支持多種服務器,或是給用戶提供部署在他們自己的服務器上的獨立的web應用的話,那么實現(xiàn)自己的Comet或是WebSocket通信系統(tǒng)會有哪些難點,該部分內容還討論了Socket.IO;第四部分內容談到了Atmosphere和CometD——最知名的用于Java技術服務器的開源反向Ajax庫。
到目前為止你已經了解了創(chuàng)建通過事件來通信的組件,在本系列的最后一部分內容中,我們把事件驅動開發(fā)的原則應用到實踐中,構建一個示例性的事件驅動web應用。
你可以下載本文中使用的源代碼。
前提條件
理想情況下,要充分體會本文的話,你應該對JavaScrpit和Java有一定的了解,并且要有一些web開發(fā)經驗。若要運行本文中的例子,你還需要最新版本的Maven和JDK(參見參考資料)。
術語
你可能對事件驅動架構(event-driven architecture,EDA)、EventBus系統(tǒng)、消息系統(tǒng)、復雜事件處理(complex event processing,CEP)和信道(channel)這些說辭并不陌生,這些術語和概念已出現(xiàn)多年。隨著技術的成熟,你可能會更頻繁地聽到這類說法。本節(jié)內容為這些概念提供一些簡短的解釋。
事件(event)
在系統(tǒng)中會發(fā)生的一些事情的出現(xiàn),事件通常具有屬性,比如說出現(xiàn)的時間(時間戳)、來源或位置(我們點擊的組件),以及一些描述事件的數(shù)據(jù)。根據(jù)系統(tǒng)的不同,事件還可以有其他的一些屬性選擇。
事件驅動架構(Event-driven architecture,EDA)
也稱作基于事件的編程,這是一種架構設計,在這種設計中,應用由通過發(fā)送和接收事件來通信和執(zhí)行的組件構成。Swing的圖形化用戶界面(GUI)就是一個EDA例子,每個Swing組件都可以監(jiān)聽事件、對事件作出反應、發(fā)送其他事件等。EDA由幾個部分組成:事件生產者、事件消費者、事件和處理軟件。
1. 事件生產者(event producer)——該組件發(fā)出事件。在本文的例子中,表單的提交按鈕就是一個事件生產者。
2. 事件消費者(event consumer)——監(jiān)聽特定事件的組件。例如,例子中的表單提交這種情況,瀏覽器監(jiān)聽表單的提交按鈕上的點擊操作,把表單數(shù)據(jù)發(fā)送給服務器。
3. 事件處理軟件(event-processing software)——這是系統(tǒng)的核心,事件生產者發(fā)布事件,事件消費者注冊自身以接收事件。根據(jù)軟件的不同,處理過程可以很簡單(只是把接收到的事件轉發(fā)給消費者),或很復雜(CEP)。有了CEP,軟件就可以支持各種各樣的處理方式,比如說事件的匯集、過濾和轉換等。
Esper就是這樣的一個軟件例子。事件處理軟件不僅可以表現(xiàn)成一個獨立的運行應用,其還可以是整合到你的應用中的庫。
消息系統(tǒng)(messaging system)
一種事件驅動應用類型,在這種應用中,事件生產者把消息發(fā)布到信道中,事件消費者則通過信道進行訂閱。事件生產者和消費者彼此之間沒有鏈接,是完全獨立開來的。在這種類型的事件驅動應用中,通常用到的術語是消息(message)而不是事件(event)。
信道(channel)
消息系統(tǒng)中分類事件的一種方式。其代表了事件生產者希望事件發(fā)送到的目的地。例如,在一個聊天室應用中,某個信道可能會是 /chatapplication/chatrooms/asdrt678,該信道標識了一個事件生產者可以發(fā)送消息的特定的聊天室,圖形化的組件應該要訂閱該信道,目的是顯示最新到達的消息。
某些消息系統(tǒng)提供了兩種類型的信道:隊列(queue)和主題(topic)。
1. 隊列(queue)——當某條消息被發(fā)送到隊列中時,只有一個事件消費者拿到并處理該條消息,其他消費者不會看到它。隊列可被持久化,以保證交付。最好的隊列例子是郵遞請求,某個web應用在用戶注冊時發(fā)布一條消息到隊列 /myapp/mail/user-registration中,可能有多個郵件應用訂閱了這一隊列,如果沒有的話,消息也不會丟失。
2. 主題(topic)——當某條消息發(fā)送到某個主題上時,每個訂閱者都可以接收到它,主題通常是沒有持久化的。一個例子是監(jiān)控軟件的一個主題/event/system/cpu/usage,生產者定期往其中發(fā)送CPU的使用情況;另一方面,這一主題可能會沒有或是有多個訂閱者,這取決于他們的興趣所在。
發(fā)布/訂閱(publish/subscribe)
事件驅動的解決方案實現(xiàn)了發(fā)布/訂閱模式。事件生產者在處理軟件中發(fā)布事件,事件消費者通過訂閱來接收它們。事件消費者訂閱的方式依賴于軟件。在消息應用中,它們訂閱信道(比如說,還可以有選擇地把過濾規(guī)則應用在事件類型上)。使用諸如Esper一類的CEP,可通過類SQL的請求來定義你所感興趣的事件,完成訂閱操作。
為什么使用事件驅動的解決方案
在一個傳統(tǒng)的通信方案中,如果系統(tǒng)A需要來自系統(tǒng)B的信息,就會發(fā)送一個請求給B。系統(tǒng)B會處理該請求,系統(tǒng)A則會停在那里等待響應。在處理完成時,響應會送回給系統(tǒng)A。在這一同步的通信模式中,資源的消耗是低效的,因為在等待響應時浪費掉了處理時間。
在異步模式中,系統(tǒng)A會從系統(tǒng)B中訂閱它響應的信息。然后系統(tǒng)A可以選擇性地給系統(tǒng)B發(fā)送通知,并立刻返回,系統(tǒng)A可以繼續(xù)處理其他事情,這一步驟是可選的。通常情況下,在事件驅動的應用中,你不需要請求其他系統(tǒng)發(fā)送事件,因為你不知道它們是誰。當系統(tǒng)B發(fā)布響應時,系統(tǒng)A會立刻接收到。
事件驅動架構的一個優(yōu)點是其允許更好的伸縮性??缮炜s性是系統(tǒng)在滿足其目標的同時適應需求、容量或是強度變化的能力。通過消除暫停時間,事件驅動的架構通常有著更好的表現(xiàn),以及有更高的處理效率。
另一個優(yōu)點表現(xiàn)在應用的開發(fā)和維護方面。使用事件驅動的解決方案,應用的每個組件都可以是完全獨立和解耦的。
事件驅動的解決方案允許有更好的反應時間,因為通信有著更低的延遲。
把事件驅動的解決方案應用在web上
web框架過去依賴于傳統(tǒng)的請求-響應模式,這導致了頁面的刷新。隨著Ajax、反向Ajax以及諸如CometD和Atmosphere一類的功能強大的框架的出現(xiàn),現(xiàn)在把事件驅動架構的概念應用到web上來獲取解耦、可伸縮性和反應性的好處已經不是什么難事了。
客戶端
事件驅動架構可應用在GUI開發(fā)的客戶端。與創(chuàng)建一個傳統(tǒng)的web頁面不同,你可以把一個單獨的web頁面當作容器使用。每個組件(頁面的每個組成部分)都可以是獨立的,你可以在web上放一個Java Swing GUI,就像包含了小工具(gadget)的Google頁面那樣(參見參考資料中的鏈接)。
你需要一個事件總線(event bus),例如,你需要開發(fā)一個JavaScript事件總線,其允許每個頁面組件從信道訂閱或是在信道中發(fā)布。事件也可以是異步的,在兩個或是多個事件到達后才觸發(fā)行為。事件總線可以用于頁面中的局部事件,但你也可以通過使用CometD或是Socket.IO來以插件的方式支持遠端事件。
服務器端
在服務器端,你需要設置一個反向Ajax框架來支持事件驅動的框架。在本系列前幾部分的考察中,發(fā)現(xiàn)只有CometD有著事件驅動的方法。對于其他框架來說,你需要增加自定義的支持,這不是什么大問題。你還可以加入第三方的消息系統(tǒng),比如說JMS(例如Apache ActiveMQ)或是像Esper那樣的CEP。一個更簡單的解決方案是Redis,其支持基本的發(fā)布/訂閱。
這一文章系列談論的是事件驅動的web和反向Ajax,因此我們重點關注客戶端,不會去設置一個復雜的消息系統(tǒng)。
事件驅動web的例子
本文將要創(chuàng)建的例子是一個聊天室web應用,該應用使用一個用戶面板來列出連接的用戶。你的用戶名是加粗顯示的,活動用戶(20秒鐘后還處活躍狀態(tài)的那些)是綠色顯示的,20秒鐘后處于非活動狀態(tài)的那些是橙色顯示的。如果有用戶連接或是斷開連接,列表就會刷新。
出于安全目的,web.xml文件中配置了兩分鐘的會話超時,非活動狀態(tài)兩分鐘后,就會彈出一個窗口,你會被重定向到登錄頁面。
只要你不再處于會話中或是還未連接,就會被重定向到登錄頁面。登錄頁面要求輸入用戶名并會查看是否可讓你登錄到聊天室中。
一旦登錄成功,你就可以在聊天室中給所有用戶發(fā)送消息。一個控制臺也會顯示出來,記錄所有收到的事件。
該web應用是基于事件的,有了上述的信息,你可以很容易地定義幾個事件:
1. 用戶連接
2. 用戶斷開連接
3. 會話過期
4. 接收到聊天消息
5. 如果沒有登錄的話,安全過濾器阻攔請求
6. 用戶變成非活動的
7. 用戶變成活動的
8. 所有其他與UI協(xié)調相關的事件
某些事件只局部于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’,
alt="" border="0" />
你可以下載這一示例應用,許多類都是安全通道類,或是會話和用戶管理通道類。本文給出了代碼最重要的部分,不過建議你下載并運行該應用例子來更加深入地了解它的運作方式。
該web應用有不同的組件構成:聊天室、用戶列表和控制臺。每個都很獨立,可以拿掉而不會影響到其他部分。
為了以局部的和遠端的方式來設置這一事件驅動的系統(tǒng),該例子使用了Ovea的EvenBus系統(tǒng)。其提供了一個局部的事件總線,一個活動遠端事件的ComeD橋接,以及一種協(xié)調事件的方式(在幾個事件完成之后觸發(fā)行為)。當然,你可以選擇使用另一個不同的系統(tǒng)。該例子使用了JavaScript來進行設置,如清單1所示。
一旦總線就位了之后,應用和組件就是基于事件的了。在本例子中,設置的是IDLE檢測系統(tǒng),如清單2所示。
清單2. IDLE檢測系統(tǒng)
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系統(tǒng)就會在檢測到活動時發(fā)送事件。這一代碼可用在任何需要IDLE系統(tǒng)的應用中。在該例子中,你需要在用戶活動的遠端事件中轉化一下該代碼。其也可用JavaScript來實現(xiàn),如清單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系統(tǒng)的事件,然后把用戶狀況發(fā)送給服務器端。其他的訂閱接收來自服務器端的用戶狀況事件。因此,只要用戶的狀況發(fā)生改變,用戶列表中的用戶的顏色就會變成綠色或是橙色。
當用戶連接或是斷開連接時,就會發(fā)送一個事件,如清單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的許多技術,你可以快速地創(chuàng)建事件驅動的web應用。不過,因為可以使用其他的系統(tǒng)來代替它,因此這并不是必需的。該例子只花了一天的開發(fā)時間,其中一半的代碼都是管道代碼,包括:
1. Maven:構建工程
2. 安全功能(登錄、退出、會話超時)
3. 使用了Jersey的REST服務
結束語
該文章系列展示了如何構建響應式的以及是可伸縮的應用,這些應用能夠提供很好的用戶體驗。事件驅動的web應用還是一個相當新的概念,某些WEB框架在內部用到了這些概念。不過本文展示的是不需要web框架來構建的這樣的應用,對于要分離Java開發(fā)者和web設計者之間的職責來說,使用好的、專業(yè)的庫就已是綽綽有余的了。關于如何把事件驅動的開發(fā)變成你的日常工作的一部分,我希望你已經有了一個較為深入的理解。它很容易讓人沉迷于其中,一旦經過嘗試,你就不想再回退到傳統(tǒng)的框架上了。
下載
描述 名稱 大小 下載方法
文章的源代碼 reverse_ajaxpt5_source.zip 925KB HTTP
關于下載方法的說明