隨著Inte.net服務的發展,其分布越來越廣泛,這使得企業消息傳遞逐漸成為Web應用基礎架構最重要的部分之一。應用程序可以在短時間內傳輸大量的消息,而且通常傳輸的數據是底層的業務流程非常必需的。 但是,無論這些數據有多么重要,很多企業應用程序的配備不足以使它們在遇到常見的消息傳遞問題時恢復正常。當然,消息可以保存在數據存儲區中,但系統常常缺少可靠且足夠靈活的監控和管理機制——像失敗消息的適當處理這么重要的功能也可能被忽略掉。 本文將從管理的角度討論WebLogic消息傳遞和Java消息服務(Java Message Service,JMS)。我們將看看如何給予業務關鍵型消息以應有的關注,以及如何使用管理工具確保應用程序的消息流順暢無阻。 企業應用程序中介體 企業消息傳遞基本上全是異構系統間的數據交換,是一種計算機間通信形式,它始終都需要專門的措施以確保其成功運行。如果缺少某種中介體,那么維護跨網絡的進程間通信會非常困難,因此我們使用諸如WebLogic JMS之類的面向消息中間件(message-oriented-middleware,MOM)系統來為我們解決保證交付、消息通知以及其他所有的固有復雜問題??梢詫OM視為企業應用程序的郵政服務。 Weblogic JMS是一種高度可靠的服務,如果它對已發送的消息做出確認,那么用戶就完全可以確信該消息已被接收。但是,確切地說,保證就到此為止了;客戶端應用程序(消息生產者)無法了解接收端應用程序(消息消費者)是否已成功地處理了該消息,除非用戶實現自己的確認系統。 如果存在問題,那到底是什么問題呢? 只要兩臺計算機進行通信,那么遲早會出現故障。無法訪問子系統、網絡故障、消息溢出、死鎖等等,這些全是用戶可能遇到的一些常見問題。不要試圖保證永遠不會發生故障,而是要確保做好應對故障的準備。需要針對每一種處理失敗的情況找出其各自的糾正措施。通過自動重試可以很容易地修復某些問題(如:子系統忙),而另外一些問題則需要人工干預。況且,許多服務不支持任何形式的重新處理或錯誤處理等,這使得故障恢復必須由客戶端應用程序來完成。 我們假設:用戶的業務模型將首先使用戶盡一切可能處理消息,不行的話才會放棄并向客戶端應用程序發出否定確認。在這種情況下,用戶可能要自動重試幾次處理;若無效,那么應有人能夠對失敗消息做人工檢查?,F在來看一種在實踐中達成此目的的常見方法。 消息管理的兩種方法 一種非常健壯的消息管理技術是將全部的失敗消息發送至一個錯誤隊列,并讓消費者將其包括的數據和屬性保存到一個數據庫中。隨后,管理員會對該消息進行分析,并選擇是否要重新創建消息,然后再次發送。使用數據庫來存儲消息使得數據具有高度完整性且操作方便。下面是該實現所需的主要步驟: 1.在config.xml文件中為目的地配置一個持久性錯誤隊列。錯誤隊列又稱為死消息隊列,失敗消息將被自動發送到這個常規JMS目的地。通過在用戶的當前目的地(active destination)中添加ErrorDestination參數可將消息定向至錯誤隊列。 2.為錯誤隊列配置消費者,通過使用javax.jms.Message類的aclearcase/" target="_blank" >ccessor方法使其讀取已存儲的失敗消息的數據和屬性。 3.將恢復性數據與消息的原始目的地名稱一起保存到一個數據庫表格中??梢酝ㄟ^使用Message類的getJMSDestination()方法獲得原始目的地。 4.編寫幾個方法,以便從數據庫讀取數據,顯示數據,通過根據已保存的數據重新創建消息、并將其發送至原始目的地,從而提供重新發送消息的選擇。 完成上述步驟后,就可以得到一個非常靈活的管理消息傳遞問題的系統。該實現需要大量的工作,但是如果用戶熟悉JMS并了解其基本知識,那么執行起來就十分簡單。雖然這種解決方案非常不錯并且經常被使用,但是還有一種更易于實現的方法,它無需使用數據庫,并且可以為用戶提供額外的好處,即,可以使用戶在運行時看到任何隊列的內容。 用于瀏覽隊列的QueueBrowser JMS API中包括支持全面訪問排隊消息的工具。下面要討論的工具是javax.jms.AueueBrowser接口及消息選擇器(message selector)。QueueBrowser是一種可在給定隊列中檢索消息而不消費這些消息的接口。它可返回全部消息,在消息選擇器的幫助下還可以返回全部消息的一個子集。 另一種可供選擇的隊列管理方法是使用消極錯誤隊列來存儲失敗消息,并使用QueueBrowser列出這些失敗消息。然后可挑選出單個消息,對其進行重新發送、復制或刪除操作?! ?/P>
下文是關于開發簡單的處理失敗消息的隊列管理系統的分步說明: 在config.xml文件中為目的地配置持久性錯誤隊列。不需要為錯誤隊列配置或編寫任何消費者。 使用QueueBrowser接口顯示錯誤隊列中的全部消息。對隊列中消息的瀏覽非常簡單,只需編寫幾行代碼即可實現(見清單)。 清單1:瀏覽隊列內容 public Enumeration getQueuedMessages( 當用戶選擇了一條消息后,可使用消息ID作為消息選擇器的參數標準來檢索該消息(見清單2)。在檢索消息后,用戶可以很容易地將其重新交付至原始目的地甚至其他的消極隊列,以便安全地保管該消息。還可以使用具有相同消息選擇器參數的QueueReceiver來刪除該消息(見清單3)。用戶可在Sun J2EE API文檔關于javax.jms.Message接口的部分中找到關于JMS消息選擇器的詳細介紹?! ∏鍐?:獲得一個隊列中的消息而無需消費它 public javax.jms.Message getMessageFromQueue( return 清單3:通過消費一個消息而從隊列中刪除它 public void deleteMessageFromQueue( qConnection.start(); 在這種方法中,應用容器負責保存用戶的消息,從而將用戶從繁冗的工作中解放出來。因為一直都在使用JMS Message對象,所以還可免掉許多乏味的數據轉換工作。 為確保一切都達到最佳狀態,請不要忘記使用RedeliveryLimit和RedeliveryDelayOverride參數在config.xml文件中分別設置適當的重發限制與重發時延。 除開發消息管理系統外,還應考慮構建合適的監控工具來監視目的地。雖然WebLogic控制臺可提供所需的大部分統計數據,但仍存在許多原因使得用戶應該通過自己的管理系統來獲得這些數據——這樣可使用戶有更多的控制權,況且對生產系統控制臺的訪問通常會受到限制。 Java管理擴展(Java Management Extensions,JMX)框架使得服務器統計數據的讀取變得非常簡單。通過使用JMX,用戶可以始終了解服務器的內部運行情況,而JMS正是眾多易于通過JMX進行監控的服務之一。 使用JMX監控JMS目的地 WebLogic Server完全支持JMX,這意味著通過JMX可監控幾乎全部的WebLogic功能。如果您還沒有這樣做過,那么打印一份您的服務器上的所有MBean(表示托管資源的JMX對象)的清單,您將驚奇地發現有如此豐富的信息可供使用。下面是一種非常好的辦法:用一個單獨的進程輪詢所有最為關鍵的MBean,并將所得到的值與預定義的警告等級進行比較?! ?/P>
要檢索JMS統計數據,首先需要獲得希望監控的目的地的JMS MBean,比如weblogic.management.configuration.JMSQueueMBean和weblogic.management.runtime.JMSDestinationRuntimeMBean。這兩個類包含若干用來從服務器中查找JMS統計數據的accessor方法。例如,只需指定一些內容,就可以獲得目的地所接收的字節總數或當前排隊的字節數。若要了解如何獲得和使用MBean的詳細信息,可查閱WebLogic文檔。 結束語 企業消息傳遞已經無處不在。無論您正在構建哪種服務,不久以后您就很可能需要在其他服務之間或者只是在集群內的進程之間共享數據。JMS是面向這種任務的事實上的行業標準。如果您還不能肯定它的所有優點,請參閱我在WLDJ 1月/2月版中的文章,題目為“Distributing Tasks in a Clustered Environment Using JMS”,您將了解到JMS遠遠不止是針對異步數據傳輸。該文章可在WLDJ網站中的Archives部分找到(Vol:4 Iss: 1)。 無論以何種方式實現企業消息傳遞,都需要為應對復雜問題做好準備。如果對數據完整性有嚴格的要求,就請確保進行了適當的監控,并且具有經過認真斟酌的處理消息傳遞問題(如:消息處理失?。┑牟呗?。您可以很方便地獲得合適的工具,而且它們都易于使用,因此,當遇到消息傳遞問題時就沒有理由不做好準備了。
javax.jms.QueueSession qSession,
javax.jms.Queue queue)
throws javax.jms.JMSException
{
javax.jms.QueueBrowser browser =
qSession.createBrowser(queue);
return browser.getEnumeration();
}
javax.jms.QueueSession qSession,
javax.jms.Queue queue ,String messageId)
throws javax.jms.JMSException
{
javax.jms.QueueBrowser browser =
qSession.createBrowser(
queue, "JMSMessageID = '" + messageId + "'");
(javax.jms.Message)
browser.getEnumeration().nextElement();
}
javax.jms.QueueConnection qConnection,
javax.jms.QueueSession qSession,
javax.jms.Queue queue, String messageId)
throws javax.jms.JMSException
{
javax.jms.QueueReceiver qReceiver =
qSession.createReceiver(
queue, "JMSMessageID = '" + messageId + "'");
qReceiver.receive(1000);
}
原文轉自:http://www.anti-gravitydesign.com