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

來源:互聯網
上載者:User

標籤:

英文原文: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. 匯流排設定

 

[javascript] view plaincopy 
  1. bus = {   
  2.     local: new EventBus({  
  3.         name: ‘EventBus Local‘  
  4.     }),   
  5.     remote: EventBus.cometd({  
  6.         name: ‘EventBus Remote‘,  
  7.         logLevel: ‘warn‘,  
  8.         url: document.location.href.substring(0,  
  9.             document.location.href.length -  
  10.             document.location.pathname.length) + ‘/async‘,  
  11.         onConnect: function() {  
  12.             bus.local.topic(‘/event/bus/remote/connected‘).publish();  
  13.         },  
  14.         onDisconnect: function() {  
  15.             bus.local.topic(‘/event/bus/remote/disconnected‘).publish();  
  16.         }  
  17.     })   
  18. };  

 

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

  圖1. 樣本應用

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

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

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

  一旦匯流排就位了之後,應用和組件就是基於事件的了。在本例子中,設定的是IDLE檢測系統,如清單2所示。                

  清單2. IDLE檢測系統

[javascript] view plaincopy 
  1. bus.local.topic(‘/event/dom/loaded‘).subscribe(function() {  
  2.     $.idleTimer(20000);  
  3.     $(document).bind(‘idle.idleTimer‘, function() {  
  4.         bus.local.topic(‘/event/idle‘).publish(‘inactive‘);  
  5.     });  
  6.     $(document).bind(‘active.idleTimer‘, function() {  
  7.         bus.local.topic(‘/event/idle‘).publish(‘active‘);  
  8.     });  
  9. })  

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

  清單3. 使用者活動管理

[javascript] view plaincopy 
  1. bus.local.topic(‘/event/idle‘).subscribe(function(status) {  
  2.     bus.remote.topic(‘/event/user/status/changed‘).publish({  
  3.         status: status == ‘active‘ ? ‘online‘ : ‘away‘  
  4.     });  
  5. });   
  6. bus.remote.topic(‘/event/user/status/changed‘).subscribe(function(evt) {  
  7.     if(evt.user != me.name) {  
  8.         $(‘#users li‘).filter(function() {  
  9.             return evt.user == $(this).data(‘user‘).name;  
  10.         }).removeClass(‘online‘)  
  11.           .removeClass(‘away‘)  
  12.           .addClass(evt.status);  
  13.     }  
  14. });  

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

  當使用者串連或是中斷連線時,就會發送一個事件,如清單4所示:

  清單4. 使用者列表管理

[javascript] view plaincopy 
  1. bus.remote.topic(‘/event/user/connected‘).subscribe(function(user) {  
  2.     $(‘#users ul‘).append(row(user));  
  3. });   
  4. bus.remote.topic(‘/event/user/disconnected‘).subscribe(function(evt) {  
  5.     $(‘#users li‘).filter(function() {  
  6.         return evt.user == $(this).data(‘user‘).name;  
  7.     }).remove();  
  8. });  

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

  1. Maven:構建工程

  2. 安全功能(登入、退出、會話逾時)

  3. 使用了Jersey的REST服務

  結束語 

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

  代碼下載

  reverse_ajaxpt5_source.zip

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.