自從被引入EJB 2.0規范后,消息驅動bean (MDB)已經成為幾乎所有企業java項目的基礎。MDB擁有一個簡單而整潔的API,用戶不需要創建或生成home和local/remote接口,所有這些使得Java開發社區迅速且廣泛地接受了MDB。如您所知,幾乎任何大型J2EE項目都有一個重要部分是與其它系統的集成有關的。一種集成不同系統的可取方法是使用面向消息中間件(MOM)提供的企業隊列(queue)和主題(topic)基礎架構,這要求所有的J2EE服務器都擁有自己的兼容JMS的MOM實現。
看起來大部分企業J2EE項目取決于JMS消息的接收和/或發送。因此,J2EE架構設計者和開發者就一定要熟悉不同的消息消費和生產方法,因為這可能最終決定項目的成功或失敗。最近,開始出現一些MDB模型的替代方案,例如SPRing和ActiveMQ的消息驅動POJO。當前的MDB模型除了本身是一項有趣有用的技術外,其地位也得以確定,它足夠簡單,并且在J2EE社區中得到了廣泛的采用。
本文主要關注基于MDB的傳統消費模型。本文將展示如何利用一種更高效的非事務型消息檢索來重構這一場景,而同時仍然保持業務用例的once-and-only-once服務質量(QoS);換句話說,業務代碼應該只處理消息一次,并且不應該丟失任何消息。這是最嚴格的服務質量,也是實現中最有趣的部分。
可以說,在大多數的J2EE項目中,只要出現消費JMS消息的用例,都會使用MDB。除了最普通的情況外,假如從業務角度來看丟失傳入的消息和/或處理消息副本可以接受,這些MDB都使用容器治理事務(CMT)劃分模型和事務型屬性RequiresNew來部署。為了使這些設置可以生效,應當用事務型的QueueConnectionFactory(或TopicConnectionFactory)來配置MDB。在這樣的設置中,消息消費過程可以用下面的步驟來描述:
如您所見,事務型消息消費的處理非常健壯。這樣的處理模型保證了once-and-only-once的服務質量;換句話說,消息要么被成功地處理一次,要么根本不被處理(可能在超出預定義的重試次數或生存時間或者轉移到停用(dead)消息隊列之后,被從隊列中移除)。
事務型消息消費模型盡管健壯且成功,但是它還是有一些嚴重的缺點。首先,分布式事務嚴重地影響了處理性能(當從本地事務切換到XA時,性能降低50%是不足為奇的)。
第二個缺點是由CMT造成的,實際的事務提交發生在應用程序代碼之外,在onMessage()調用返回之后。這也許沒什么大不了的(究竟CMT的整體思想是要將應用程序從事務處理中解脫出來),但是存在一些令人不愉快的問題——一些錯誤情況直到事務提交后才進行偵測。例如,在BEA WebLogic Server中,默認情況下,所有由處理CMP bean(創建,更新等等)引起的DML操作都被延遲到事務提交階段。這意味著應用程序可以認為它成功地更新了CMP bean的一個實例,而實際上實際的SQL更新可能會因為違反數據庫的某種約束而失敗。最糟糕的是應用程序代碼不能夠對此做出反應或者只是恰當地進行記錄,因為它看不到異常。
盡管對延遲的DML操作有一個應急方案(例如,在BEA WebLogic Server中,可以在部署描述符中禁用它),但是這伴隨著性能損失。J2EE服務器將不能再聚集和/或批處理SQL更新(以便執行更高效),或者將它們全部忽略(假如事務稍后被標記為回滾的話)。
本文認為,采用一種bean治理事務(bean-managed transaction,BMT)方法可以提供同樣的服務質量,并對事務生命周期有更多的控制。應用程序代碼將有機會恢復和/或更清楚地報告錯誤,同時避免上述的CMT模型的所有缺點。此外,我們預料從事務作用域中移除消息檢索能帶來重大的性能提升。
在討論BMT方法之前,我們需要分析在這種情況下從隊列中消費消息會發生什么。假如我們使用BMT 劃分部署MDB,J2EE服務器不會再把MDB監聽的JSM目的地(隊列或者主題)添加到事務中(事務將會在從隊列中取走消息之后開始)。在這種情況下,BMT MDB應當用部署描述符中的非XA連接工廠配置;否則J2EE服務器會部署失敗。
根據JMS規范(JMS1.1第一節4.5.2),假如使用AUTO_ACKNOWLEDGE 或者 DUPS_OK_ACKNOWLEDGE模式非事務型地部署消息監聽器,并且onMessage()方法拋出RuntimeException或它的任何子類,則消息會被重新發送。換句話說,重新設計用例來使用BMT是有可能的,假如在處理消息時出錯的話,應用程序代碼可以拋出RuntimeException,消息就會被重新發送(重試)。這種方法很有效,因為使用RuntimeException來表示不可恢復的錯誤是很自然的(例如Spring Framework的異常層次結構基本上全是基于RuntimeException的子類)。消息會重新發送,直到達到一定的次數(可在MOM軟件層配置),之后它通常被丟棄或轉移到停用消息隊列,或者應用程序代碼會計算消息被重新發送的次數,并決定什么時候應當停止嘗試處理以及不處理就消費(假如適當的話,會產生錯誤消息)還是轉移到另外的隊列。
新聞熱點
疑難解答