廢話就不要看了,看看線程和command的部分設計就可以了,還不錯的想法。
http://dev2dev.bea.com.cn/techdoc/webser/2005060104.html在請求驅動的環境中解耦合和延遲處理是創建健壯和可伸縮的分布式應用程序的關鍵戰略之一。許多服務都單獨依賴于集群來確保可伸縮性,但是當新發現的需求使應用程序的復雜性增長時,它們常常會遇到麻煩。
盡管服務器集群是推動可伸縮性的基本技術,但是當所有的處理都同步完成時,它可能變得很低效。吞吐量可能會增加,但是響應性卻會變得不可救藥。
在本文中,我討論了異步處理,并舉例說明了,巧妙的任務管理會如何提高您應用程序的性能、可用性、可伸縮性和可管理性。我們將以一種高度可配置的方式,創建一個一般的任務分配框架,該框架可以發送任何任務給您的集群中的一臺或每臺服務器。通過使用多態和 Java 消息服務( Java Message Service , JMS ),我們的框架將實現著名的 Command 模式。
解耦合在實際中的意義
當服務器收到客戶端請求時,它通常需要在返回響應之前執行幾個單獨的任務。解耦合( decoupling )意味著不會一次執行所有的任務,而是把一些任務放入隊列并異步地進行處理。因為排隊通常是一項低開銷的操作,同步的請求會更快地結束。
解耦合的優點
按照順序和并行地處理任務,通常會比隨機地處理它們要更加高效(客戶端偶然發出請求的時候)。正面的影響比隨即表現出來的要大。理論上,解耦合可以提高以下各個方面的性能:
- 健壯性 : 提高,因為請求將依賴的可能失效的過程更少。
- 響應性 : 請求的部分后處理減少了接收請求和返回響應之間的時間。
- 可伸縮性 : 所有解耦合后的過程在復雜性方面可能會增加,但不會有降低響應度的危險。
- 可用性 : 可以在服務器永遠不知道故障原因的情況下處理故障。
在子系統不可用的情況下,配置自動重試要更加容易。
自然,理論與實踐之間的差別在每個應用程序上的體現各不相同。然而,顯然,幾乎每種實現都至少具有前述的某些優點。
解耦合的缺陷
和大部分好東西一樣,解耦合也存在缺點。其中最嚴重的缺點之一就是,如果您不能確保您擁有足夠強大的硬件來清空繁忙的處理隊列,那么您可能會發現,它的可用性實際上降低了。如果進入的異步請求比您的系統能夠處理的要多,隊列就會非常迅速地增長。您必須注意設計過程,而對隊列實行自動監控無疑是可取的做法。另一個明顯的問題是,在請求驅動的環境中,大多數過程都不是很適合于解耦合。事實上,大多數處理都可能被要求返回響應。有時,它會需要一些開箱即用的思想,甚至可能是您為您的客戶端所提供的服務方式的變化。
哪些過程可以解耦合
從純技術的角度來講,幾乎所有的過程都可以解耦合。例如,您可以把一個所購買物品及客戶詳細信息的清單放入隊列,從而可以解耦合一個訂單事務——異步處理將負責余下的工作。不足之處在于您不能在響應中包含任何處理細節。因此,謹慎地預先驗證數據是很重要的,這樣可以確保不會出現問題。
一種越來越流行的實現是,馬上把請求放入隊列,然后持續輪詢服務器,以便了解何時可以獲得響應。盡管這種方法在本質上實際是同步的,而且不會增加請求的處理時間,但是它在心理上具有優勢,因為可以在輪詢期間顯示進度條。
除了解耦合完整的業務邏輯(這是一個巨大的難題)之外,更少的集中處理,比如日志記錄和發送電子郵件,是可以考慮的良好選擇。當性能變得及其重要時,沒有理由讓客戶端等待這種任務的完成。特別的電子郵件是用于解耦合的一個良好選擇。讓我們做進一步的了解。
實例研究:異步的電子郵件
以傳統的方式發送電子郵件(作為同步請求的一部分)會引起一些問題。首先,連接到電子郵件服務器需要一次網絡往返,速度可能會很慢,尤其是在服務器非常繁忙的時候。過載的電子郵件服務器甚至可以使依賴于電子郵件的服務暫時不可用。
XA 事務支持
另一個顯而易見的問題是,電子郵件服務器通常在本質上是非事務性的。當事務被回滾時,這可以導致出現不一致的通知——把一條消息放入隊列之后不能取消它。幸運的是, JMS 支持事務,而且可以通過把消息的發送延遲到提交底層事務的時候來解決這個問題。
考慮訪問數據庫和事務感知的 JMS 時,您將需要使用 XA 和兩階段提交( 2PC )事務。可以使用非 XA 資源來模擬 XA ,但是您可能會得到不一致的數據。啟用 XA 只是一個配置問題,而且通常不需要修改代碼。參見 WebLogic 文檔以獲得相關的詳細信息。
通過 JMS 發送電子郵件
為了使用 JMS 來發送電子郵件,我們需要配置 JMS 組件(比如, JMS 服務器, JMS 隊列和連接工廠)。我們還需要編寫一個消息驅動 Bean ( Message Driven Bean , MDB )來執行信件的實際發送。當我們想要在我們的代碼中發送電子郵件時,需要創建一條包含信件的屬性和內容的 JMS 消息。之后,我們把它發送給處理隊列。
這樣做的工作量很大!幸運的是, BEA WebLogic JMS 為我們提供了創建一個可以解耦合幾乎任何過程的框架所需的全部內容。
用于異步執行的框架
是動手看看一些代碼的時候了。我們將創建一個框架,這個框架支持在集群中的一臺或所有服務器上,異步地執行代碼的任何部分。實現起來的確需要花費些力氣,但是一旦框架完成,異步執行就是再容易不過的事情了。
這個思路是編寫一些類,這些類包含一個帶有可運行代碼的公共方法和另一個用于初始化參數的方法——可能是構造器。封裝在 JMS 對象消息中之后,這些預先編寫好的類的實例(命令消息)就被發送給在您的服務器上配置的 JMS 隊列。至此,消費者把它們取出,然后異步地執行它們(參見圖 1 )。
讓我們逐個看看這個框架的所有部分:
- JMS 隊列 : 應該在每臺服務器上配置一個用于接收命令消息的 JMS 隊列。還應該為重復保存故障消息配置錯誤隊列。
- JMS 連接工廠 : 為了支持事務性行為的運行時選擇,應該配置兩個連接工廠:一個支持 XA ,而另一個不支持 XA 。
- 命令對象接口 (CommandMessage) : 這是一個所有命令對象都需要實現的簡單 Java 接口。它擴展了 java.io.Serializable 接口,該接口對于在 JMS 對象消息中嵌入我們的命令來說是必需的。現在,因為我們想要在不知道命令的確切類型的情況下運行它們,我們還要實現 java.lang.Runnable 接口,稍后把它們簡單地轉換為 Runnable 對象,并執行它們的運行方法。我們在不知道我們運行的確切內容的情況下運行了代碼。這是最理想的多態。
- 命令執行程序 (CommandExecutionManager) : 我們將使用一個 MDB 來處理命令。實例池化防止了 JMS 初始化重復出現,這使得 MDB 成為功能非常強大的消息監聽器,非常適合于這項任務。編寫 Bean 類不需要很大的工作量,我們只需要在 onMessage 方法中編寫數行代碼(參見清單 1 )。
這樣就把收到的消息傳遞給一個 ObjectMessage ,獲得嵌入的命令對象,然后執行它的運行方法。通過在 config.xml 文件中,把隊列的重新發送限制設置為一個大于 0 的值,您可以配置一個重試計數器。從您的命令對象拋出一個運行時異常,便可觸發重新發送的動作。此外,通過配置重新發送延遲,您還可以控制重試的頻率。
用于發送消息的一個幫助器類 (TaskDistributor)
從技術上說,這個部分并不是完全必要的,每次都可以手動進行 JMS 排隊。然而,這是一個冗長乏味的過程,而且實際上是幫助器使這個框架變得如此實用。幫助器是一個常規的 Java 類,帶有用于對命令消息進行排隊的靜態方法。您可以針對處理不同的場景編寫單獨的方法,但是為了簡明起見,我選擇了編寫一個可以處理大多數情況的方法:
static void execute(CommandMessage cm, long delay, b oo lean runEverywhere, b oo lean persisted, b oo lean
enableXA, int priority)
這個靜態方法帶有幾個用于精確執行控制的參數。讓我們逐個討論這些參數:
- CommandMessage cm : 一個命令消息實例。
- long delay : 代表發送屬性的時間,借助 weblogic.jms.extensions.WLMessageProducer 類進行設置。這樣,就可以在夜間或者其他方便的時間執行命令。接受一個 Date 對象也是可以的。
- b oo lean runEverywhere : 決定是否發送要執行的消息給集群中的一臺隨機選中的訪問器或者所有的服務器。
- b oo lean persisted : 將通過使用隊列發送程序的 setDeliveryMode 方法選擇發送模式。應該始終保持業務關鍵型的消息,從而在訪問器崩潰的時候,這些消息不會丟失。然而,持久性始終是以性能損失為代價的,這也應該納入考慮的范圍內。
- b oo lean enableXA : 將選擇方法是否使用支持 XA 的 JMS 連接工廠。此參數設置為 true 時,排隊將參與底層事務(如果存在的話),在提交事務之前不會對消息進行排隊。
- int priority : 決定消息的 JMS 優先級。在發送之前,將使用給定的值調用 javax.jms.Message 類的 setJMSPriority 方法。有效的范圍是 0-9 。對于大多數應用程序來說,給命令消息指派不同的優先級似乎有些過頭,但是出于完整性方面的考慮,我還是在這里包括了這個選擇。
應該針對您的特定執行的需要,來量身打造 TaskDistributor 幫助器類的實現。在本文中,要包含一個例子似乎太長了,但是您可以從 WLDJ Web 站點 www.sys-con.com/wldj/sourcec.cfm 下載一個。還可以添加幾個額外的參數來控制更高精度的執行,但是另一方面,您可能已經滿足于較少的幾個選項。
使用異步執行
框架完成之后,我們就可以開始實現我們的命令消息了。讓我們看一個簡單的例子。首先,我們需要創建一個代表我們的命令消息的類(參見清單 2 )。為了調用執行,我們使用了 TaskDistributor 類(參見清單 3 )。
當調用例子中的執行方法時,一個包含 DistributedLogger 類實例的 ObjectMessage ( JMS 優先級被設置為 4 )將在 1 秒延遲之后,被發送給集群中所有的服務器。隨后,記錄程序將在所有的服務器上打印一個字符串給 stdout 。借助已經完成的框架,異步執行變得非常易于實現,完全沒有障礙。節點到節點的通信變得前所未有的容易。
容器托管的任務分配
我們可以通過使用池化線程和虛擬內存隊列,來創建一個類似的服務,以處理異步請求。然而,我們強烈建議讓應用服務器來管理所有的線程。
此外,因為 JMS 為我們提供了一個非常簡潔和靈活的解決方案,沒有理由不讓我們的服務器來處理錯綜復雜的過程。事實上,我們可以調用這個方法容器托管的任務分配( Container-Managed Task Distribution )。
性能問題
BEA WebLogic 可以處理很大的消息量,而且性能通常不是問題。盡管如此,當生產非常大量的命令用于處理時,還是推薦使用非持久性的消息和流水線操作。另外,消息流控制可以緩解:服務利用率的暫時高峰所導致的消息處理消耗過多資源。
并行處理
使用 MDB 的一個巨大優點是,它們可以自動地并行處理消息。您可以通過顯示池化消費者 Bean 的數量來調整消耗處理資源的數量。
WebLogic 提供大量有價值的 JMS 擴展和配置選項——它們中有許多可以用在任務分配的各種實現中。選擇和優化用于分頁、重新發送、持久性和調節(流控制)的 JMS 參數時,應該格外注意。
JMS 是一個非常完善的服務,對其功能進行仔細研究是值得的。想要了解有關提高性能的更多信息,請參見 WebLogic JMS 性能指南。
結束語
我們已經討論了解耦合和異步的消息收發。作為一條經驗法則,我們可以說,處理異步請求的服務器,比專門處理同步請求的服務器的執行效率要高。盡管解耦合并不總是件容易的事情,或者甚至不總是個可行的方法,但在考慮周全地實現時,它仍然是一種功能非常強大的機制。我們獲得的不僅僅是幾個與性能相關的優點,而且能夠設計更加靈活的應用程序。
BEA WebLogic JMS 遠遠不止是個用于數據傳輸的服務。除了可配置性非常強之外,它還提供許多有用的功能,比如自動重新發送,消息的持久化,可調度性, XA 支持,調節,瞬時分頁和循環故障消息的重定向。通過利用這種強大的通用性,我們可以創建一個功能強大而且可以擴展的框架,來處理需要進行異步處理的所有情形。
關于作者
John-Axel Strahlman 是 Sanda Interactive Ltd ( www.stcinteractive.com ) 的創立者和 CEO 。 Sanda Interactive Ltd 是一家軟件咨詢公司,總部位于芬蘭的 Espoo 。他是一位分布式系統專家,為他的客戶的項目提供咨詢已經超過 5 年。( 更多信息 )
清單 1 :命令處理器 MDB
public void onMessage(Message msg) {
ObjectMessage om =
(ObjectMessage) msg;
Runnable command =
(Runnable) om.getObject();
command.run();
}
清單 2 :命令類示例
public class DistributedLogger
implements CommandMessage {
private String text = null;
public void setText(String text) {
this.text = text;
}
public void run() {
System.out.println(text);
}
}
清單 3 :框架使用示例
DistributedLogger logger =
new DistributedLogger();
String text =
"Hello asynchronous execution!"
logger.setText(text);
TaskDistributor.execute(
logger, //Command instance
1000, //delay
true, //runEverywhere
false, //persisted
false, //enableXA
4); //delay.
原文出處
http://sys-con.com/story/?storyid=48222&de=1