延續系列第一部分對 J2EE 狀態管理技術的探討,上期討論無狀態網路管理選項,本期聚焦有狀態網路管理技術。簡介有狀態應用管理後,將比較不同方案在 Web 層與業務層的應用、優缺點,並透過常見實作與最佳實務協助企業選擇合適方案。註:本文視 JSP 為特殊類型的 Servlet。
Web 應用協議分無狀態與有狀態,狀態指協議「記憶」從一個傳輸到下一個傳輸資訊的能力。有狀態連線是企業應用基本需求,但 Web 應用依賴 HTTP(本質無狀態),開發人員發展出多種技巧在 HTTP 上模擬有狀態連線:將有狀態資訊儲於 HTML 表單欄位、附加於超連結、或儲於客戶端 Cookie。
客戶端-伺服器有狀態互動可在 Web 層或業務層管理。Web 層使用 Servlet 搭配 HttpSession API;業務層使用有狀態會話 EJB 組件。以下探究這兩種選項。
Servlet 架構的 HttpSession API 允許開發人員管理跨網路的客戶端/伺服器互動(會話)狀態。HttpSession 介面定義核心功能:識別跨多頁面請求的單一客戶端,在伺服器儲存與該客戶端關聯的資料。Servlet 容器透過此介面建立、管理會話,會話由 HttpSession 物件表示,可跨來自相同客戶端的多連線、多頁面請求持續特定時間。Servlet 可查看會話資訊(建立時間、上次存取時間),將物件綁定至會話,使資訊以跨連線持久方式關聯特定客戶端。
因此,HttpSession 介面允許容器建立管理會話,Servlet 存取會話資訊、綁定物件、存取先前綁定物件。但容器如何透過無狀態 HTTP 追蹤客戶端?每個 HttpSession 物件獲唯一標識,確保會話與資料唯一識別。考慮 HTTP 無狀態本質,每次請求客戶端必須傳遞會話標識給伺服器,容器才能關聯正確會話。會話標識可用三種方式傳遞:HTML 表單參數(通常隱藏欄位)、查詢字串參數、Cookie 屬性。無論何種方式,容器皆攔截、檢查並找到關聯的 HttpSession 物件。
Servlet 架構的輕量級執行緒模型,不會因 Servlet/JSP 建立、讀取、修改 HttpSession 物件而受損。該物件僅將物件參考儲為簡單鍵值對的雜湊表或類似集合。HttpSession 記憶體實作本身也輕量,僅需儲存(或序列化)會話物件與對應標識。簡言之,Servlet 可支援與 HTTP 客戶端的有狀態互動,且對應用設計或容器資源影響極小。
J2EE 內建支援業務層狀態處理。同無狀態會話 Bean,有狀態會話 Bean 映射至業務流程。關鍵差異:無狀態 Bean 及其資料僅存活於單一客戶端請求生命週期;有狀態 Bean 維護與客戶端對話,資料跨多請求持久。與 Servlet 不同,有狀態會話 Bean 無需特殊物件或額外介面建立有狀態連線,EJB 容器提供所有管理。對 Bean 而言,僅需在部署描述符宣告為 stateful。
會話 Bean 是最輕量企業 Bean 類型。無狀態會話 Bean 易被容器共用(僅維護單請求狀態)。反之,有狀態會話 Bean 對容器資源不友善:其池無法像無狀態 EJB 池容納任意客戶端請求。有狀態 Bean 僅處理單一客戶端請求,直到客戶端釋放控制權。它消耗大量容器時間與記憶體。為保存客戶端調用間 Bean 狀態,容器必須將 Bean 實例保持於活動記憶體,或暫時將狀態寫入持久化儲存(檔案系統或資料庫)——即「鈍化」。先前鈍化的企業 Bean 再次被請求時,容器從池檢索並利用鈍化前持久狀態初始化——即「活化」。

決定保持記憶體或鈍化/活化取決於廠商實作。鈍化機制雖釋放容器資源,但無法防止伺服器崩潰導致有狀態會話 Bean 活動狀態遺失。雖部分廠商提供會話復原功能,但非標準,仰賴會降低可移植性。EJB 規範定義 javax.ejb.SessionSynchronization 介面,可向企業 Bean 通報交易狀態(含伺服器崩潰導致的失敗交易)。實作此介面的 Bean 必須定義 afterBegin()、beforeCompletion()、afterCompletion(boolean) 三個方法,接收容器三額外回呼以正確處理 Bean 交易狀態。
從效能觀點,Servlet 與無狀態會話 Bean 具競爭力,均可用實例池服務客戶端請求。但加入應用狀態管理後,巨大效能差異浮現。不同於 Servlet 架構輕量級 HttpSession 機制,有狀態會話 Bean 需重量級狀態管理解決方案(鈍化/活化方案)。此方案耗費伺服器時間資源鈍化 Bean 狀態、回收實例、活化狀態,每過程需多次容器調用及直接對 Bean 執行回呼方法確保狀態正確處理。總之,有狀態會話 EJB 組件提供管理應用狀態的重量級機制。
與無狀態 J2EE 架構不同,有狀態應用無典型配置當藍圖。合適架構取決於:
雖部分問題似傾向某技術,但許多有狀態方案實際需同時使用 Servlet 與 EJB。關鍵決定在於:在 Web 層、業務層、或兩層同時管理狀態。以下探討企業應用場景與最適解決方案。
先決定是否需要有狀態互動模型。管理有狀態會話 Bean 資源密集,應考慮輕量替代方案。最佳方案:直接用 RMI 與應用伺服器的無狀態會話 Bean 對話。若必須有狀態,考慮帶簡單交易層的無狀態會話 Bean,或在業務層建立瘦 Servlet 層。任一方案以部分成本提供有狀態體驗。若客戶端必須與跨多請求業務流程狀態緊密耦合且無法接受增加 Web 層,有狀態會話 Bean 是顯而易見的選擇。
狀態管理目標不同。若目標透過 GUI 提供流暢體驗,可在 Web 層管理狀態;若目標串聯跨多請求的複雜業務流程,狀態管理應在業務層。同樣應探索其他選項,如帶交易層的無狀態會話 Bean。
部分應用伺服器廠商以接受本地 IIOP 呼叫方式公開 EJB 容器,允許 CORBA 客戶端視 EJB 為本地 CORBA 應用。非 Java 客戶端可用 IIOP 協定直接與無狀態會話 Bean 通訊,繞過 Web 層直接與業務層溝通。架構分析同防火牆後基於 Java 應用客戶端,參考第一種場景。
上月討論無狀態會話 Bean 為此類應用精心設計:輕量、易池化、確保卓越擴展性。有狀態會話 Bean 非為此設計。此類應用通常需狀態管理,但最好由專用機制或 J2EE 交易處理。另一種可能:調用 EJB 如 CORBA 組件,當整合應用為 CORBA 組件時特別有用。
三種基本「富」GUI(非 HTML、非命令列)客戶端:Java Applet、獨立應用、Java Web Start。以下方案適用三者:
標準基於 Web 應用場景中,客戶端位於防火牆哪側不重要;使用 Servlet 是必然。因使用 HTTP 傳輸協定,將在 Web 層工作。唯一實際決定——是否在幕後使用 EJB 組件——取決於對 EJB 容器服務的相關需求。先選擇通用組件類型(Servlet、會話 Bean),再選擇匹配 UI 顯示與業務請求處理的特定類型(JSP、有狀態會話 Bean)。狀態管理方面存在類似問題,標準問答助您確定方案:
HttpSession API 管理狀態。HttpSession。HttpSession API 持久化 JavaBean 實例。SessionSynchronization 介面允許無狀態/有狀態會話 Bean 回應交易故障,可回滾交易、持久化資料或執行其他清理。通常伺服器故障不會阻礙容器調用介面宣告的回呼方法。HttpSession 實作。若伺服器支援,可用 JavaBean 作為業務委派。需結合基於 Web 瀏覽器與標準「富」GUI 桌面客戶端時,有狀態選項與無狀態選項無異。參考系列第一篇文章獲取詳細資訊。
本文(J2EE Pathfinder 系列第二部分)探討使用 Java Servlet 與有狀態會話 Bean 執行客戶端請求、提供有狀態體驗的優缺點。討論場景雖不盡所有情況,但代表有狀態通訊環境中 Servlet 與會話 EJB 組件最常見用法。
下一部分將開始持久資料管理兩部分探討,先比較實體 Bean 與 JDBC。願探索愉快!