在 J2EE 探索系列的 第一部分中,我們首先研究了 J2EE 中狀態管理的最新技術。上個 月,我們討論了 J2EE 中管理無狀態網絡的最佳選項;這個月我們將討論管理有狀態網絡的 技術。
首先我將簡要介紹有狀態應用程序管理,然後談論不同的解決方案如何應用於 Web 層或 業務層。接下來,我將比較 J2EE 中有狀態應用程序管理技術的優缺點。就象在前一部分中 一樣,我們將通過研究每種技術最常用的一些實現,以及用於為您的企業選擇合適解決方案 的一些最佳實踐來結束本文。
請注意,對於本文而言,JSP(Java ServerPages)文件被認為是專用類型的 servlet。
有狀態應用程序管理
您可能會回想起來,在上一篇文章中,Web 應用程序協議被分成兩大類別: 無狀態 (stateless)和 有狀態(stateful),協議的 狀態指的是它“記憶”從一個傳輸到下一個 傳輸的信息的能力。因為有狀態連通性是大多數企業應用程序的基本需求之一,並且因為 Web 應用程序依賴於 HTTP(內在的無狀態協議),所以聰明的開發人員已經找到了許多技巧 來在 HTTP 上模擬有狀態連接。有狀態信息可以存儲在 HTML 表單字段中、附加到超鏈接或 者存儲在客戶機端的 cookie 中。
客戶機和服務器之間的有狀態交互可以在 Web 層或業務層上進行管理。要在 Web 層上管 理狀態,我們使用與 HTTPSession API 結合的 servlet。要在業務層上管理狀態,我們使用 有狀態會話 EJB 組件。在接下來的章節裡,我們將探究這兩種開發選項。
Web 層
Servlet 體系結構的 HTTPSession API 允許應用程序開發人員管理跨網絡的客戶機/服務 器交互(或 會話)的狀態。 HTTPSession 接口定義了 HTTPSession API 的核心功能。它為 J2EE 應用程序提供了一種方法,使它可以識別跨多個頁面請求的單個客戶機,以及將數據存 儲在與那個客戶機相關聯的服務器上。通過該接口,servlet 容器創建和管理客戶機和服務 器之間的會話。該會話由 HTTPSession 對象表示,它可以跨來自相同客戶機的多個連接和頁 面請求持續存在一段特定的時間。Servlet 使用該接口來查看與處理有關會話的信息,如創 建時間和上一次訪問會話的時間。該接口還允許 servlet 將對象綁定到會話,從而以一種跨 多個連接(來自相同客戶機)持續存在的方式將該信息與特定的客戶機進行關聯。
因此, HTTPSession 接口允許 servlet 容器創建和管理客戶機會話,並且使 servlet 能訪問與會話相關的信息、將對象綁定到會話以及訪問先前綁定的對象。到現在為止,一直 都還不錯。但是 servlet 容器如何跟蹤通過無狀態協議(如 HTTP)通信的客戶機呢?為了 實現這一點,為每個 HTTPSession 對象都提供一個唯一的標識,以確保每個客戶機會話和與 會話相關的數據可以被唯一標識。考慮到 HTTP 內在的無狀態本質,在每次請求時,該會話 標識必須被客戶機傳遞給服務器,以便於 servlet 容器將客戶機與正確的會話相關聯。會話 標識可以用三種方式中一種進行傳遞:作為 HTML 表單中的參數(通常是隱藏字段);作為 附加在查詢字符串後的參數;或者作為 cookie 的屬性。不管會話標識如何傳遞,servlet 容器都將攔截它,檢查它,並找到與之關聯的 HTTPSession 對象。
Servlet 性能
由 Servlet 體系結構創建的輕量級線程模型決不會因為 servlet 或 JSP 文件創建、讀 取或修改 HTTPSession 對象而受到破壞。該對象只是將對象引用存儲為簡單鍵-值對的散列 表或類似的集合。同樣, HTTPSession 內存空間的實現本身也是輕量級的,只需要存儲(或 許序列化)會話對象和相應的會話標識。簡而言之,servlet 可以支持與 HTTP 客戶機的有 狀態交互,而且對應用程序設計或容器資源產生最小的影響。
業務層
J2EE 為在業務層上處理狀態提供了內置的支持。與無狀態會話 bean 一樣,有狀態會話 bean 也被映射到業務過程。兩者之間的關鍵區別是:無狀態 bean 及其數據在單個客戶機請 求的生命周期內存活,而有狀態 bean 卻維護與客戶機的對話並且它們的數據跨多個請求持 續存在。與 servlet 不同,有狀態會話 bean 不需要任何特殊的對象,也不需要使用額外的 接口來創建有狀態連接。EJB 容器提供了所有有狀態會話 bean 管理。對於 bean 而言,所 有必要的工作就是在其部署描述符中將其聲明為 stateful 。
管理有狀態 bean
正如以前闡述的,會話 bean 是最輕量級類型的企業 bean 類型。特別地,無狀態會話 bean 可以方便地被容器合用,因為它們只需要維護每個請求的狀態。
相反,有狀態會話 bean 與容器資源並不那樣友好。有狀態會話 bean 的池不能象無狀態 EJB 組件的池那樣用來容納任何客戶機請求。有狀態 bean 只能處理來自一個客戶機的請求 ,直到該客戶機釋放其對那個特殊 bean 實例的控制。有狀態會話 bean 消耗了容器的大量 時間和內存。為了保存客戶機調用之間的 bean 狀態,容器必須將 bean 實例保存在活動內 存中,或者臨時將狀態寫到持久性存儲(如文件系統或數據庫)中。將狀態分配到持久性存 儲中就是所謂的 鈍化(passivation)。當以前鈍化的企業 bean 被再次請求時,容器通過 從池中檢索 bean,並且利用鈍化前 bean 的持久性狀態對它進行初始化,來激活它。下圖闡 明了有狀態會話 bean 的鈍化和激活:
圖 1. 有狀態會話 bean 的鈍化/激活
決定將 bean 保存在內存中還是對它進行鈍化,然後再激活它,這取決於各個供應商。盡 管在釋放容器資源方面鈍化機制非常有幫助,但是在防止服務器崩潰以避免丟失有狀態會話 bean 的活動狀態方面卻無能為力。盡管一些供應商提供了會話恢復功能以解決這個問題,但 它不是標准的,因此依賴該功能會降低應用程序的可移植性。但是,別擔心!EJB 規范確實 定義了一個接口( javax.ejb.SessionSynchronization ),它可以向企業 bean 警報事務 的狀態,包括由於服務器崩潰而失敗的事務(假定不是拔了服務器的插頭)。實現 SessionSynchronization 接口的企業 bean 必須定義三個已聲明的方法特征符: afterBegin() 、 beforeCompletion() 和 afterCompletion(boolean) 。這些方法使 bean 可以從容器接收三個額外的回調,以允許正確處理 bean 中的事務狀態。
EJB 組件性能
從性能的角度看,servlet 和無狀態會話 bean 是相當具有競爭力的技術。它們都可以使 用實例池為來自客戶機的請求提供服務。但是,當您添加了應用程序狀態管理時,巨大的性 能差異就將顯現。與可用作 Servlet 體系結構一部分的輕量級 HTTPSession 機制不同,有 狀態會話 bean 需要一個更加重量級的針對狀態管理(如上面概述的鈍化/激活方案)的解 決方案。這個針對有狀態會話 bean 的常用解決方案需要花費服務器時間和資源來鈍化 bean 狀態、回收 bean 實例和激活 bean 狀態。上述的每個過程都需要幾次容器調用,以及需要 直接對 bean 執行回調方法,以確保正確處理 bean 的狀態。總而言之,有狀態會話 EJB 組 件為管理應用程序狀態提供了一種重量級機制。
選擇合適的技術
與無狀態的 J2EE 體系結構不同,J2EE 應用程序不提供典型的配置來充當指南或藍圖。 管理狀態時,合適的體系結構取決於下列因素:
客戶機是基於 Web(HTTP)的嗎?
對話狀態需要包含到 GUI 中嗎?
服務器將處於哪種負載條件下呢?
有狀態組件需要能夠在服務器崩潰後仍然有效嗎?
該組件需要哪種事務上下文呢?
有狀態數據有多重要?
盡管上述一些問題似乎明顯地傾向於其中一種技術,但是許多有狀態方案實際上既需要使 用 servlet 又需要使用 EJB 組件。至關重要的決定就是確定是在 Web 層還是在業務層上管 理狀態,或者同時在兩個層上管理狀態。在下一節中,我們將研究一些可能的企業應用程序 方案,及其最適宜的解決方案。
應用程序客戶機
標准的應用程序客戶機是與另一個系統或組件相互操作的客戶機。我們將研究三種典型的 應用程序客戶機方案,並且討論每個客戶機最適合的有狀態解決方案:
如果客戶機是基於 Java 的,並且與服務器處於相同的防火牆之後,那麼您首先應該決定 有狀態交互模型是否是必需的。管理有狀態會話 bean 是資源密集型的,因此您應該考慮更 輕量級的備用方案。最佳解決方案就是使用 RMI 直接與應用程序服務器中的 無狀態會話 bean 對話。如果 有狀態解決方案是必需的,則請考慮使用帶有簡單事務層的無狀態會話 bean,或者在業務層上創建瘦 servlet 層。任何一種解決方案花費部分成本即可提供有狀態 體驗。最後,如果您的客戶機必須與跨多個請求的業務過程的狀態進行緊耦合,並且不能接 受添加 Web 層,那麼有狀態會話 bean 是顯而易見的選擇。
如果您正在使用非 Java 的客戶機或者使用與服務器不在同一個防火牆之後的客戶機,那 麼狀態管理問題略有不同。在這種方案中,您首先應該確定狀態管理的目標。如果目標是通 過某種 GUI 為用戶提供流暢的體驗,那麼您可以在 Web 層上管理狀態。如果目標是將跨多 個請求的復雜業務過程聯系在一起,那麼狀態管理應該在業務層上進行。再次強調,您應當 始終探索其它選項,如使用帶有事務層的無狀態會話 bean。
一些應用程序服務器供應商以一種諸如接受本機 IIOP 調用的方式公開 EJB 容器,從而 允許 CORBA 客戶機將 EJB 組件當成本機 CORBA 應用程序。這允許非 Java 客戶機使用 IIOP 協議與無狀態會話 bean 進行通信。在該設置中,客戶機繞過了 Web 層,並使用 IIOP 協議直接與業務層(會話 bean)進行通信。這時,體系結構分析與位於防火牆後的基於 Java 的應用程序分析是相同的。請參考第一種方案,以理解業務層上的狀態管理問題。
電子商務隨需應變環境
正如我們 上個月討論的,無狀態會話 bean 是為電子商務隨需應變(e-business on demand)應用程序精心設計的。它們是非常輕量級的,可以輕松地匯聚為池,以確保卓越的 可伸縮性。相反,有狀態會話 bean 並不是為這類應用程序而精心設計的。電子商務隨需應 變應用程序中通常需要狀態管理,但是最好由專用的機制或通過 J2EE 事務進行處理。另一 種可能性是調用 EJB 組件,就好象它是 CORBA 組件一樣。當一個或多個被集成的應用程序 是 CORBA 組件時,該選項特別有用。
“富”GUI 客戶機
有三種基本的“富”GUI(不是 HTML,也不是命令行)客戶機類型:Java applet、獨立 應用程序和 Java Web Start。下列解決方案適用於這三種“富”GUI 組件類型中的任何一種 :
如果您的客戶機和服務器被防火牆分隔,您應該讓客戶機通過 HTTP 與 servlet 直接通 信。該 servlet 層可以使用助手類應付簡單的業務處理。如果您的應用程序有更復雜的需求 ,或者對企業資源有更高的請求頻率,您應該使用會話 bean 來處理業務過程。這裡再強調 一次,您應當將有狀態交互模型的必要性作為決策過程的一部分進行考慮。
如果您的客戶機和服務器位於同一個防火牆之後,直接的 RMI 調用可能是您的最佳選擇 。在這種情形下,servlet 只會帶來額外的開銷和不必要的體系結構復雜性。在 applet 或 Java Web Start 情形中,通過提供帶有 applet 或 Java Web Start 應用程序鏈接的首個 HTML 文檔,servlet 可以啟動事務。然後,最好使客戶機與一個或多個會話 bean 建立直接 的 RMI 連接。該方案的設置與典型的應用程序客戶機方案(與服務器不在同一個防火牆之後 )的設置相同。
如果您正在使用本機 GUI 客戶機,並且需要管理復雜的事務或事務系列,那麼您應該再 次考慮調用 EJB 組件,就象它是 CORBA 組件一樣。如果不能那樣做,您可以始終讓客戶機 通過 HTTP 與 servlet 通信,並且相應地管理會話。
Web 應用程序
在標准的、基於 Web 的應用程序情形中,客戶機位於防火牆的哪一側並不重要;使用 servlet 是必需的。因為您將使用 HTTP 作為傳輸協議,所以將在 Web 層上工作。唯一實際 的決定 ― 是否在幕後使用 EJB 組件 ― 將取決於對 EJB 容器服務的相關需求。首先,您 將選擇通用的組件類型(如 servlet 和會話 bean)。接下來,您將選擇一些匹配應用程序 的用戶界面顯示和業務請求處理需求的更特定類型(如 JSP 頁面和有狀態會話 bean)。就 所關心的狀態管理而言,Web 應用程序中存在著和其它客戶機類型中類似的問題。一些標准 問題(和答案)將有助於您為您的 Web 應用程序確定合適的狀態管理解決方案:
有狀態體驗需要直接與用戶界面聯系嗎? 如果是,那麼必須使用 servlet 和 HTTPSession API 管理狀態。
您的業務過程需要跨越來自客戶機的多個調用嗎? 如果是,您有三種選項:
使用有狀態會話 bean 來提供有狀態業務過程。
使用無狀態會話 bean,並且讓持久數據高速緩存在服務器上。接著對高速緩存的引用可 以存儲在客戶機的 HTTPSession 對象中。
通過調用 bean 上的方法使 JavaBean 充當業務委派(請參閱 參考資料),從而使 bean 批處理所有來自客戶機的數據,並且僅在客戶機准備提交業務過程時才聯系無狀態會話 EJB 組件。再次使用了 HTTPSession API 來持久化 JavaBean 實例。
您的有狀態業務過程需要故障保護嗎? 如果是,您有三種選項:
使用應用程序服務器,該服務器為有狀態會話 bean 的狀態恢復提供自動支持。
SessionSycnhronization 接口允許無狀態或有狀態會話 bean 對事務故障做出響應。這 將允許您回滾事務、持久化數據或執行您可能需要的任何其它清除功能。通常,服務器故障 將不會阻礙容器調用由接口聲明的回調方法。
許多 Web 服務器都提供了故障保護 HTTPSession 實現。如果您的服務器可以這樣做,那 麼您可以使用 JavaBean 作為業務委派。
多客戶機類型
最後的情形需要客戶機類型的組合,例如基於 Web 的浏覽器和標准的“富”GUI 桌面。 在這種情形下,有狀態選項同無狀態選項沒有區別。請參考 本系列的第一篇文章以獲取詳細 信息。
結束語
在本部分( J2EE Pathfinder系列的第二部分)中,我們探討了使用 Java servlet 和有 狀態會話 bean 來執行客戶機請求和提供有狀態體驗的相對優缺點。本文討論的方案並未包 含所有情形,但是它們代表了有狀態通信環境中的 servlet 和會話 EJB 組件的一些最常見 用法。
在下一部分中,我們將開始有關持久數據管理的兩部分探討,首先將比較實體 bean 和 JDBC。願我們到時“探索”愉快!