本文概述了如何使用 Java 消息傳遞系統(tǒng)(JMS)進(jìn)行大型文件的復(fù)制。Dan Drasin 描
述了解決 Applied Reasoning 公司客戶的分布式數(shù)據(jù)問題的方案,并提供了基于 JMS
的解決方案的實(shí)現(xiàn)細(xì)節(jié)。他討論了其中的優(yōu)點(diǎn)、一些潛在缺陷以及將 IBM MQSeries(現(xiàn)
在稱為 WebSphere MQ)成功設(shè)置為 JMS 服務(wù)器的一些實(shí)際指示信息。
背景
在思考消息傳遞解決方案時(shí),您可能會(huì)想到一個(gè)通過遠(yuǎn)程消息調(diào)用機(jī)制來集成兩個(gè)不同
應(yīng)用程序的系統(tǒng)。一般來講,對于不常通信的分布式實(shí)體以及數(shù)據(jù)傳輸量不是很多這樣
的情況,常常使用這種耦合。較經(jīng)典的示例是,連接到異構(gòu)后端和入口的同構(gòu)接口,這
些后端和入口指派進(jìn)行用戶請求的后端處理,然后為最終用戶表示而對那些請求進(jìn)行重
新格式化。
消息傳遞方法中的公共線程一直有這樣的假定:雖然消息傳遞解決方案在系統(tǒng)之間提供
健壯、高度可用的通信,但它基本上效率很低,只用來作為在無法避免與外部系統(tǒng)通信
時(shí)的最后一種手段。在出現(xiàn)遠(yuǎn)程方法調(diào)用(RMC)時(shí)關(guān)于消息傳遞的這種觀點(diǎn)就開始流行
一直到出現(xiàn)了更現(xiàn)代的象 CORBA 和 DCOM 那樣的消息傳遞解決方案,而且,通常所應(yīng)用
的消息傳遞只局限于解決幾類問題。
目標(biāo)
在過去的十年中,人們對分布式系統(tǒng)需求有了更深入的理解。新興技術(shù)(象 Java 和 .
NET)已經(jīng)包含了代碼分布來作為它們基本編程模型的一部分。通過這樣做,這些技術(shù)已
將高度可用性和容錯(cuò)性融入到消息傳遞中,同時(shí)鼓勵(lì)那些提供解決方案的供應(yīng)商交付一
些系統(tǒng),這些系統(tǒng)在更廣范圍的問題上考慮性能。
近來我們公司被要求實(shí)現(xiàn)文件分布和復(fù)制的解決方案,在以前這樣的方案需要集成安全
的 FTP、數(shù)據(jù)庫復(fù)制和其它一次性解決方案的定制系統(tǒng)。我們沒有一味地埋頭按照定制
開發(fā)的道路前進(jìn),而是研究了將最新的消息傳遞解決方案應(yīng)用到這個(gè)問題的可能性。我
們發(fā)現(xiàn) JMS 不僅為信息傳送提供必要的基礎(chǔ)結(jié)構(gòu),而且它還能處理我們客戶要求的、與
服務(wù)質(zhì)量、安全性、可靠性和性能有關(guān)的所有基礎(chǔ)結(jié)構(gòu)問題。本文描述了我們團(tuán)隊(duì)面臨
的挑戰(zhàn),以及 JMS(以 MQSeries 的形式)如何讓我們滿足并超越客戶的要求。
問題
我們的客戶面臨一個(gè)重大的分布式數(shù)據(jù)難題,在全國范圍內(nèi)有許多呼叫中心,在全國各
地的呼叫中心里接線員要記錄與客戶之間的交互。必須快速可靠地在遠(yuǎn)程數(shù)據(jù)中心為這
些記錄建立索引并存檔。建立索引和存檔的存儲(chǔ)過程不能影響接線員的系統(tǒng)記錄和存儲(chǔ)
接線員正在與客戶交互的信息的能力。該客戶已經(jīng)有了一個(gè)包含組合起來的代碼、VPN
和其它技術(shù)的系統(tǒng)。但是,現(xiàn)有的解決方案遠(yuǎn)遠(yuǎn)達(dá)不到性能和可靠性上的目標(biāo),并且它
是一種拙劣的技術(shù),難以理解并且維護(hù)費(fèi)用很高。
在開發(fā)替代客戶原有系統(tǒng)時(shí),我們考慮了 JMS 和多種非 JMS 的解決方案,尤其是那些
基于 FTP 和安全復(fù)制(SCP)的解決方案。然而,非 JMS 解決方案有兩個(gè)主要缺點(diǎn):
它們對于安全性方面的缺陷一籌莫展。(FTP 上的安全性漏洞已經(jīng)人人皆知,并且人們
對此已廣泛地作了記載。如果需要這方面的例子,請參閱參考資料。)
它們提供的基礎(chǔ)結(jié)構(gòu)只適用于實(shí)際的數(shù)據(jù)傳送,而對于處理可靠性、容錯(cuò)性、安全性、
平臺(tái)獨(dú)立性以及性能優(yōu)化等問題,需要定制開發(fā)來解決。
我們團(tuán)隊(duì)最后得出結(jié)論,對于添加這些額外的特性所需的開發(fā)工作是讓人望而卻步的,
因此我們決定選用 JMS 解決方案,它可以擺脫這些問題。
解決方案
我們開發(fā)了一個(gè)基于 JMS 的系統(tǒng),它:
為已記錄的多媒體文件提供可靠存檔
支持可擴(kuò)展性,可以使多個(gè)數(shù)據(jù)中心接收文件
支持對其它數(shù)據(jù)類型進(jìn)行存檔
我們這里正討論的文件比以前那些涉及消息傳遞解決方案的項(xiàng)目中傳送的數(shù)據(jù)還要大(
50K - 500K)。我們第一個(gè)任務(wù)是確保數(shù)據(jù)大小不會(huì)影響 JMS 解決方案。通過測試系統(tǒng)
傳遞各種大小的消息有效負(fù)載時(shí)的性能,我們評估了包括 IBM MQSeries 在內(nèi)的許多 J
MS 解決方案。結(jié)果顯示:經(jīng)過適當(dāng)配置,大小達(dá)到 1 兆的消息不會(huì)對整個(gè)系統(tǒng)性能產(chǎn)
生顯著影響。因?yàn)槌WR認(rèn)為消息傳遞解決方案只適用于定期的、小的有效負(fù)載,所以我
們的結(jié)果是一個(gè)重大發(fā)現(xiàn)。我們繼續(xù)分析系統(tǒng)的體系結(jié)構(gòu)(圖 1 中概述了此體系結(jié)構(gòu))
,它可以提供客戶需要的安全性、高可用性和可靠性。
圖 1. 高級系統(tǒng)體系結(jié)構(gòu)
現(xiàn)有的基礎(chǔ)結(jié)構(gòu)在每個(gè)客戶機(jī)上有一個(gè)系統(tǒng),當(dāng)接線員與用戶之間進(jìn)行交互時(shí),它創(chuàng)建
多媒體文件,以此作為響應(yīng)。此外,還需對這些文件進(jìn)行存檔。我們的系統(tǒng)啟動(dòng)一個(gè)進(jìn)
程(運(yùn)行在每個(gè)機(jī)器上)并在已知目錄中查找這些文件。當(dāng)檢測到新文件時(shí),進(jìn)程將它
們打包成 JMS 有效負(fù)載并發(fā)送到其中一個(gè)數(shù)據(jù)中心的 JMS 服務(wù)器以便傳遞。一旦 JMS
服務(wù)器確認(rèn)收到,則除去發(fā)送方中的這些文件。JMS 服務(wù)器將該數(shù)據(jù)傳送到數(shù)據(jù)中心內(nèi)
的一個(gè)可用處理程序上,進(jìn)行存檔。
主要概念
JMS 是特定于 Java 的消息傳遞和排隊(duì)的實(shí)現(xiàn)。在消息傳遞和排隊(duì)中有兩個(gè)基本思想:
系統(tǒng)通過使用不連續(xù)的數(shù)據(jù)包進(jìn)行通信,這些數(shù)據(jù)包都有一個(gè)有效負(fù)載(即要傳送的信
息)和屬性(即該信息的特征以及它應(yīng)如何通信)。這個(gè)數(shù)據(jù)包稱為 消息。
消息不是被發(fā)送給系統(tǒng),而是被發(fā)送到一個(gè)獨(dú)立的保存區(qū)域。可以根據(jù)您的需要確定保
存區(qū)域的數(shù)量,通過唯一的名稱,可以標(biāo)識并定位它們。每個(gè)保存區(qū)域都可以接收消息
,并且根據(jù)配置的不同,該區(qū)域?qū)⒚總€(gè)消息要么傳遞給所有感興趣的系統(tǒng)(發(fā)布-訂閱)
,要么傳遞給第一個(gè)感興趣的系統(tǒng)(點(diǎn)對點(diǎn))。這個(gè)保存區(qū)域稱為 目的地。
我們構(gòu)建的系統(tǒng)采用點(diǎn)對點(diǎn)的目的地,在 JMS 中稱為隊(duì)列。排隊(duì)是圖 1 中顯示的系統(tǒng)
設(shè)計(jì)的一個(gè)重要方面。該圖顯示了消息正從 JMS 代理直接傳送到接收方的客戶機(jī)上,但
這并不十分準(zhǔn)確。實(shí)際上,消息被傳送到一個(gè)隊(duì)列中,接收方客戶機(jī)從隊(duì)列中檢索它們
。稍后我們研究實(shí)現(xiàn)細(xì)節(jié)時(shí),這個(gè)區(qū)別將變得非常重要,因?yàn)樗屜到y(tǒng)并行地處理收到
的消息。
跨平臺(tái)和交叉供應(yīng)商
對我們客戶機(jī)來說盡量減少對某家供應(yīng)商的依賴,這意味著,我們所設(shè)計(jì)的代碼應(yīng)該使
由于更改了 JMS 供應(yīng)商而帶來的影響降至最低,這是十分重要的。JMS 的一個(gè)主要優(yōu)點(diǎn)
是它以廣泛的業(yè)界支持和開放標(biāo)準(zhǔn)為基礎(chǔ),因此有了正確設(shè)計(jì)的代碼,我們就可以讓系
統(tǒng)使用任何 JMS 系統(tǒng)。(可以對現(xiàn)有系統(tǒng)進(jìn)行直接改進(jìn),專門設(shè)計(jì)來使系統(tǒng)在某套硬件
上運(yùn)行并能與特定于供應(yīng)商的解決方案相匹配。)
通過將所有特定于供應(yīng)商的調(diào)用封裝在稱為 JMSProvider 的類中,就可以輕松實(shí)現(xiàn)平臺(tái)
獨(dú)立性。這些 Provider 類處理特定于供應(yīng)商的問題,例如工廠查詢、錯(cuò)誤處理、連接
創(chuàng)建和消息特性設(shè)置等。請參閱下面清單 1 中的示例代碼。
清單 1. 在類 ar.jms.JmsProvider 中
public QueueConnection createConnection() throws JMSException {
return getConnectionFactory().createQueueConnection(getUserName(),
getPassword());
}
通過利用“Java 命名和目錄接口(JNDI)”,我們將特定于供應(yīng)商的設(shè)置存儲(chǔ)在一個(gè)資
源庫(例如,LDAP 庫)中,這樣實(shí)際代碼就幾乎不需要特定于供應(yīng)商的引用。只需要少
量特定于供應(yīng)商的代碼來處理一些特性,但是可以將這樣的代碼限定于一些“適配器”
類,并將它保存在應(yīng)用程序代碼之外。請參閱下面清單 2 中的示例代碼。因?yàn)?JMS 被
設(shè)計(jì)用來方便地使用 JNDI,所以與其它解決方案相比,這是另一個(gè)直接優(yōu)點(diǎn) ― 配置信
息的集中存儲(chǔ)不僅可以保存基于文本的信息,而且還可以存儲(chǔ)已配置的對象。
清單 2. 在類 ar.jms.JmsProvider 中
public final static String
CONNECTION_FACTORY_LOOKUP_NAME_KEY = "CONNECTION_FACTORY_LOOKUP_NAME";
public final static
String FILE_TRANSFER_QUEUE_LOOKUP_NAME_KEY =
"FILE_TRANSFER_QUEUE_LOOKUP_NAME";
public final static String
JMS_PROVIDER_CLASS_KEY = "JMS_PROVIDER_CLASS";
public void init() throws NamingException {
InitialContext jndi = createInitialContext();
initConnectionFactory(jndi);
initFileTransferQueue(jndi);
}
public QueueConnection createConnection() throws JMSException {return
getConnectionFactory().createQueueConnection(getUserName(),
getPassword());
}
public void initConnectionFactory(InitialContext jndi) throws
NamingException {
setConnectionFactory((QueueConnectionFactory)jndi.lookup
(getProperties().getProperty(CONNECTION_FACTORY_LOOKUP_NAME
_KEY)));
}
public void initFileTransferQueue(InitialContext jndi) throws
NamingException {
setFileTransferQueue((Queue) jndi.lookup
(getProperties().getProperty(FILE_TRANSFER_QUEUE_LOOKUP_NAM
E_KEY)));
}
跳出傳統(tǒng)模式,JMS 解決方案允許以可靠的方式傳送消息,即一旦確認(rèn)已將消息傳送到
JMS 服務(wù)器,就將它傳送至尋址到的目的地(隊(duì)列)。MQSeries 也不例外。一旦成功
執(zhí)行了將消息發(fā)送到服務(wù)器的代碼,客戶機(jī)就保證目的地最終會(huì)接收到消息,即使所討
論的服務(wù)器在處理過程中出現(xiàn)故障(如果目的地暫時(shí)不可用,或者 JMS 服務(wù)器死機(jī)等等
)。請參閱下面清單 3 中的示例代碼。下面代碼中的類實(shí)際上負(fù)責(zé)一旦它確定需要發(fā)送
文件,就執(zhí)行數(shù)據(jù)的發(fā)送。
通過將消息配置為持久消息,我們可以保證一旦目的地(隊(duì)列)接收到消息,那么消息
將保留在那里直到它在該隊(duì)列中被檢索為止 ― 即使在系統(tǒng)有故障期間。因此,一旦安
全地將消息傳送到本地 JMS 服務(wù)器,就可以刪除它了。不能過高估計(jì)克服系統(tǒng)故障的能
力;對周期性系統(tǒng)故障的處理是開發(fā)分布式存檔解決方案最重要的問題之一。客戶現(xiàn)有
系統(tǒng)上處理故障情況的代碼很復(fù)雜很脆弱,而且對這些故障的處理和維護(hù)費(fèi)用很高。通
過一個(gè)健壯的、經(jīng)測試成功的商業(yè)解決方案,JMS 使我們能解決所有這些問題。
清單 3. 來自類 ar.jms.file.send.ConnectionElement
public void sendMessage(byte[] payload, boolean persistent) throws
SendFailedException {
QueueSender sender = null;
try {
Message message = createMessage(payload);
sender = createSender
(persistent ? DeliveryMode.PERSISTENT : DeliveryMode.NON_PERSISTENT);
sender.send(message);
getClient().getLogService().logInfo(getName() +
" sent message " + message.getJMSMessageID() + ".");
} catch (JMSException exception) {
getClient().getLogService().logError
("JMS exception processing " + getName(),exception);
stop();
throw new SendFailedException("JMS Message Send Failed");
}
try {
sender.close();
} catch (JMSException ignore) {
getClient().getLogService().logInfo(getName() + " failed to
close sender. Processing will continue.");
}
}
這個(gè)解決方案的關(guān)鍵是配置 JMS 消息和服務(wù)器來同時(shí)提供令人滿意的性能和服務(wù)質(zhì)量。
JMS 規(guī)范定義了配置選項(xiàng),并通過所有商業(yè)解決方案實(shí)現(xiàn)它們。但是,配置的確切方法
根據(jù)不同的供應(yīng)商而有所不同。
設(shè)置
我們創(chuàng)建的體系結(jié)構(gòu)和系統(tǒng)具有通用性且很強(qiáng)大。但是,對于一些移動(dòng)部件,必須使用
正確的方式配置并鉤連它們。以下內(nèi)容是有關(guān)將 MQSeries 成功地設(shè)置為 JMS 服務(wù)器的
概述、一些潛在缺陷和實(shí)際的指示信息。
對于 MQSeries,首先設(shè)置 JNDI 服務(wù)器來檢索特定于實(shí)現(xiàn)的設(shè)置,在這種情況下是 JM
S 連接工廠(JMS Connection Factory)。有許多不同方法來實(shí)現(xiàn)這個(gè)操作,但適宜的
通用選項(xiàng)是輕量級目錄訪問協(xié)議(LDAP)服務(wù)器。我們選擇使用 Qualcomm SLAPD。一旦
安裝好并運(yùn)行該服務(wù)器,就可以用 MQSeries 管理工具(JMSAdmin.bat)來設(shè)置該服務(wù)
器并將其作為 MQ 對象信息庫來使用。請參閱參考資料,獲取有關(guān)講述該過程的實(shí)用書
籍的鏈接。同時(shí),在設(shè)置期間,要特別注意在 IBM MQSeries 之上設(shè)置 JMS 的 IBM 文
檔,這很重要。這個(gè)過程涉及創(chuàng)建一些隊(duì)列和其它對象,這些隊(duì)列和對象是特定于 JMS
使用并且不屬于標(biāo)準(zhǔn) MQSeries 安裝的。
完成 JNDI/LDAP 和 JMS 服務(wù)器的設(shè)置后,就可以準(zhǔn)備配置客戶機(jī)了。第一步是理解 J
MS 如何與 IBM 的標(biāo)準(zhǔn) MQSeries 實(shí)現(xiàn)交互。MQSeries 的 Java 客戶機(jī)能以兩種模式之
一進(jìn)行交互:客戶機(jī)和綁定模式。只能通過 Java Applet 來使用客戶機(jī)模式,而綁定模
式取決于客戶機(jī)上的 DLL 或者對象庫。因?yàn)閷?shí)現(xiàn)的特性,當(dāng)使用用于 JMS 連接信息的
LDAP 服務(wù)器時(shí),只能使用綁定模式。(不清楚為什么有這個(gè)限制,但它確實(shí)存在。)
因此,將用戶登錄和密碼存儲(chǔ)在一個(gè)全局位置(com.ibm.mq.MQEnvironment.class)而
不是在連接時(shí)傳遞它們。要解決這些供應(yīng)商問題,我們創(chuàng)建了標(biāo)準(zhǔn) JmsProvider 類(稱
為 MQSeriesProvider)的子類。這個(gè)類將完成的唯一操作是覆蓋如何創(chuàng)建連接。不象清
單 1 中那樣
newQueueConnection = getConnectionFactory().createQueueConnection(getUserNam
e(),getPassword));
進(jìn)行調(diào)用,我們必須調(diào)用
newQueueConnection = getConnectionFactory().createQueueConnection();
最后,您需要將特定于 JMS 的元素(如隊(duì)列、隊(duì)列管理器、隊(duì)列工廠等等)提供給客戶
機(jī)。現(xiàn)在,使用 LDAP 和 JNDI 的原因就變得很明顯:我們使用 LDAP 服務(wù)器來存儲(chǔ)這
些元素并使用外部文件保存那些 LDAP 對象的鍵。LDAP 服務(wù)器可以作為 JNDI 服務(wù)器并
通過返回存儲(chǔ)的對象來響應(yīng)名稱查詢。這就是清單 2 中代碼的工作原理。JMS 元素的名
稱可從類的靜態(tài)變量(對于缺省名稱)或者外部文件(使用非缺省的其它名稱)中獲取
。簡而言之,向 LDAP 服務(wù)器請求鍵(我們正討論的)中存儲(chǔ)的對象,并返回我們感興
趣的 JMS 對象(在這種情況下)。
我們基于 JMS 的解決方案通過使用現(xiàn)有的組件更方便地實(shí)現(xiàn)統(tǒng)一的、跨平臺(tái)和交叉供應(yīng)
商的配置環(huán)境。現(xiàn)在我們的代碼已盡可能地成為獨(dú)立于特定于平臺(tái)和特定于供應(yīng)商的設(shè)
置。
應(yīng)用程序
應(yīng)用程序中有兩個(gè)關(guān)鍵組件:發(fā)送器和接收器。發(fā)送器啟動(dòng)一個(gè)后臺(tái)程序,它在目錄中
輪詢需要存檔的文件,而接收器只是等待將要傳遞的 JMS 消息,然后將該消息中包含的
文件存檔。JMS API 使我們幾乎無需關(guān)注正使用的特定 JMS 實(shí)現(xiàn)就可以定義這些組件。
發(fā)送器由三個(gè)主要部件組成:
JMSProvider,用于創(chuàng)建連接
ConnectionPool,用于獲取現(xiàn)有的空閑連接(我們稱之為 JMSConnection)
輪詢程序,監(jiān)視需要傳送的文件。
在啟動(dòng)時(shí),使用 JMSProvider 來創(chuàng)建一些到 JMS 服務(wù)器的準(zhǔn)備就緒的連接。這些連接
放置在池中,然后啟動(dòng)輪詢程序。當(dāng)輪詢程序檢測到需要傳送文件時(shí),它就創(chuàng)建一個(gè)獨(dú)
立線程來處理這個(gè)文件。(可以通過派生(forking),創(chuàng)建一個(gè)獨(dú)立的線程來創(chuàng)建消息
和進(jìn)行傳送操作,描述該過程非常簡單。但在實(shí)際應(yīng)用中,常常將合用(pooling)與循
環(huán)組合起來使用,這樣可以確保很少創(chuàng)建新線程,而是重用線程。但是,那個(gè)過程相當(dāng)
復(fù)雜,過多的說明會(huì)分散本文的中心主題 ― JMS。)
在獨(dú)立線程中,輪詢程序接著從連接池中獲取 JMSConnection,用它來創(chuàng)建一個(gè) Bytes
Message,并將這個(gè)文件的二進(jìn)制內(nèi)容放入那個(gè)消息中。最后這個(gè)消息查找到接收器,并
發(fā)送到 JMS 服務(wù)器,接著將 JMSConnection 返回給 ConnectionPool。這個(gè)發(fā)送過程的
部分步驟顯示在下面的圖 2 中。
圖 2. 發(fā)送器過程
接收器是一個(gè)較簡單的組件;它啟動(dòng)一些 FileListener 來等待將要放置在接收器隊(duì)列
中的消息。下面的清單 4 中的代碼顯示了 FileListener 設(shè)置處理過程。圖 6 中的類
實(shí)際上負(fù)責(zé)從隊(duì)列中檢索消息并對它們進(jìn)行存檔。JMS 保證隊(duì)列發(fā)送每個(gè)消息的次數(shù)僅
一次,所以我們可以安全啟動(dòng)許多不同的 FileListener 線程并且知道每個(gè)消息(因此
每個(gè)文件)只處理一次。這個(gè)保證是使用基于 JMS 解決方案的另一個(gè)重要優(yōu)點(diǎn)。在自己
設(shè)計(jì)的解決方案中開發(fā)這樣的功能(比如基于 FTP 的功能),花銷很大且易出錯(cuò)。
清單 4:來自類 ar.jms.file.receive.FileListener public void startOn(Queue qu
eue) {
setQueue(queue);
createConnection();
try {
createSession();
createReceiver();
getConnection().start(); // this starts
the queue listener
} catch (JMSException exception) {
// Handle the exception
}
}
public void createReceiver() throws javax.jms.JMSException {
try {
QueueReceiver receiver = getSession().
createReceiver(getQueue());
receiver.setMessageListener(this);
} catch (JMSException exception) {
// Handle the exception
}
}
public void createSession() throws JMSException {
setSession(getConnection().
createQueueSession(false, Session.AUTO_ACKNOWLEDGE));
}
public void createConnection() {
while (!hasConnection()) {
try {
setConnection(getClient().createConnection());
} catch (JMSException exception) {
// Connections drop periodically on the
internet, log and try again.
try {
Thread.sleep(2000);
} catch
(java.lang.InterruptedException ignored) {
}
}
}
}
以回調(diào)的方式編寫消息處理代碼,回調(diào)是當(dāng)將消息傳遞給 FileListener 時(shí),JMS 自動(dòng)
調(diào)用的方法。這個(gè)消息的代碼顯示在下面的清單 5 中。
清單 5. 來自類 ar.jms.file.receive.FileListener public void onMessage(Messag
e message) {
BytesMessage byteMessage = ((BytesMessage) message);
OutputStream stream =
new BufferedOutputStream(
new FileOutputStream(getFilenameFor(message)));
byte[] buffer = new byte[getFileBufferSize()];
int length = 0;
try {
while ((length = byteMessage.readBytes(buffer)) != -1) {
stream.write(buffer, 0, length);
}
stream.close();
} catch (JMSException exception) {
// Handle the JMSException
} catch (IOException exception) {
// Handle the IOException
}
}
在設(shè)置接收器時(shí)要記住一條訣竅:在所有 FileListener 啟動(dòng)后,確保啟動(dòng)這些 FileL
istener 的原始線程繼續(xù)運(yùn)行。這是必要的,因?yàn)槟承?JMS 實(shí)現(xiàn)在守護(hù)程序的線程中啟
動(dòng) QueueListener。所以,如果正在運(yùn)行的唯一線程是守護(hù)程序的線程,那么 Java 虛
擬機(jī)(JVM)可能會(huì)過早地意外退出。下面的清單 6 顯示了一些防止這種情況發(fā)生的簡
單代碼。
清單 6. 至少使一個(gè)非守護(hù)程序的線程保持運(yùn)行 public static void main(String[]
args) {
ReceiverClient newReceiverClient = new ReceiverClient();
newReceiverClient.init();
setSoleInstance(newReceiverClient);
while(!finished) { // This prevents the VM from exiting early
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
}
}
結(jié)束語
在該項(xiàng)目的最初實(shí)現(xiàn)之后,我們添加了一些功能,象消息壓縮、當(dāng)位置無法到達(dá)時(shí)的自
動(dòng)恢復(fù)、聯(lián)合消息代理、安全性、健壯的日志記錄、管理等等。添加這些功能很容易,
因?yàn)?JMS 提供了開放模型,而且我們的體系結(jié)構(gòu)也很健壯。構(gòu)建整個(gè)系統(tǒng)花了六個(gè)星期
的時(shí)間,并且還很快地替換了客戶一直使用的現(xiàn)有的、勞動(dòng)密集型的系統(tǒng)。在這些天里
,系統(tǒng)已經(jīng)超出了所有的基準(zhǔn)測試程序的標(biāo)準(zhǔn)并且已更正了原來系統(tǒng)遺留下來的錯(cuò)誤。
這個(gè)項(xiàng)目不單超出了客戶的期望,還證明了 JMS 是一個(gè)可行的解決方案,不僅適用于小
型、面向消息的應(yīng)用程序,而且還適用于大規(guī)模的、重要任務(wù)的數(shù)據(jù)傳送操作。
參考資料
獲取更多信息或者下載 Qualcomm slapd。
訪問 JMS 主頁。
回顧 FTP 的安全性問題。
查閱 Giotta 等人編寫的 JMS 設(shè)置一書:“Professional JMS Programming”。
學(xué)習(xí)有關(guān) IBM MQSeries 的更多內(nèi)容或者下載測試版本。
閱讀 developerWorks 上的相關(guān)文章:“Java theory and practice: Should you use
JMS in your next entERPrise application?”。
閱讀 developerWorks 上 Daniel 以前的文章:“The profound impact of hardware,
software, and networking decisions”。