Java消息隊(duì)列JMS實(shí)現(xiàn)原理解析
一、什么是JMS
JMS即Java消息服務(wù)(Java Message Service)應(yīng)用程序接口,是一個(gè)Java平臺(tái)中關(guān)于面向消息中間件(MOM)的API,用于在兩個(gè)應(yīng)用程序之間,或分布式系統(tǒng)中發(fā)送消息,進(jìn)行異步通信。Java消息服務(wù)是一個(gè)與具體平臺(tái)無關(guān)的API,絕大多數(shù)MOM提供商都對(duì)JMS提供支持(百度百科給出的概述)。我們可以簡(jiǎn)單的理解:兩個(gè)應(yīng)用程序之間需要進(jìn)行通信,我們使用一個(gè)JMS服務(wù),進(jìn)行中間的轉(zhuǎn)發(fā),通過JMS 的使用,我們可以解除兩個(gè)程序之間的耦合。
二、為什么需要JMS
在JAVA中,如果兩個(gè)應(yīng)用程序之間對(duì)各自都不了解,甚至這兩個(gè)程序可能部署在不同的大洲上,那么它們之間如何發(fā)送消息呢?舉個(gè)例子,一個(gè)應(yīng)用程序A部署在印度,另一個(gè)應(yīng)用程序部署在美國(guó),然后每當(dāng)A觸發(fā)某件事后,B想從A獲取一些更新信息。當(dāng)然,也有可能不止一個(gè)B對(duì)A的更新信息感興趣,可能會(huì)有N個(gè)類似B的應(yīng)用程序想從A中獲取更新的信息。
在這種情況下,JAVA提供了最佳的解決方案-JMS,完美解決了上面討論的問題。
JMS同樣適用于基于事件的應(yīng)用程序,如聊天服務(wù),它需要一種發(fā)布事件機(jī)制向所有與服務(wù)器連接的客戶端發(fā)送消息。JMS與RMI不同,發(fā)送消息的時(shí)候,接收者不需要在線。服務(wù)器發(fā)送了消息,然后就不管了;等到客戶端上線的時(shí)候,能保證接收到服務(wù)器發(fā)送的消息。這是一個(gè)很強(qiáng)大的解決方案,能處理當(dāng)今世界很多普遍問題。
三、JMS的優(yōu)勢(shì)
1、異步
JMS天生就是異步的,客戶端獲取消息的時(shí)候,不需要主動(dòng)發(fā)送請(qǐng)求,消息會(huì)自動(dòng)發(fā)送給可用的客戶端。
2、可靠
JMS保證消息只會(huì)遞送一次。大家都遇到過重復(fù)創(chuàng)建消息問題,而JMS能幫你避免該問題。
四、JMS的消息模型
JMS具有兩種通信模式:
1、Point-to-Point Messaging Domain (點(diǎn)對(duì)點(diǎn))
2、Publish/Subscribe Messaging Domain (發(fā)布/訂閱模式)
在JMS API出現(xiàn)之前,大部分產(chǎn)品使用“點(diǎn)對(duì)點(diǎn)”和“發(fā)布/訂閱”中的任一方式來進(jìn)行消息通訊。JMS定義了這兩種消息發(fā)送模型的規(guī)范,它們相互獨(dú)立。任何JMS的提供者可以實(shí)現(xiàn)其中的一種或兩種模型,這是它們自己的選擇。JMS規(guī)范提供了通用接口保證我們基于JMS API編寫的程序適用于任何一種模型。
1、Point-to-Point Messaging Domain(點(diǎn)對(duì)點(diǎn)通信模型)
點(diǎn)對(duì)點(diǎn)的模式主要建立在一個(gè)隊(duì)列上面,當(dāng)連接一個(gè)列隊(duì)的時(shí)候,發(fā)送端不需要知道接收端是否正在接收,可以直接向ActiveMQ發(fā)送消息,發(fā)送的消息,將會(huì)先進(jìn)入隊(duì)列中,如果有接收端在監(jiān)聽,則會(huì)發(fā)向接收端,如果沒有接收端接收,則會(huì)保存在activemq服務(wù)器,直到接收端接收消息,點(diǎn)對(duì)點(diǎn)的消息模式可以有多個(gè)發(fā)送端,多個(gè)接收端,但是一條消息,只會(huì)被一個(gè)接收端給接收到,哪個(gè)接收端先連上ActiveMQ,則會(huì)先接收到,而后來的接收端則接收不到那條消息。
在點(diǎn)對(duì)點(diǎn)消息傳送模型中,應(yīng)用程序由消息隊(duì)列,發(fā)送者,接收者組成。每一個(gè)消息發(fā)送給一個(gè)特殊的消息隊(duì)列,該隊(duì)列保存了所有發(fā)送給它的消息(除了被接收者消費(fèi)掉的和過期的消息)。
(1)模式圖:
(2)特點(diǎn):
每個(gè)消息只有一個(gè)消費(fèi)者;
發(fā)送者和接收者在時(shí)間上是沒有時(shí)間的約束,也就是說發(fā)送者在發(fā)送完消息之后,不管接收者有沒有接受消息,都不會(huì)影響發(fā)送方發(fā)送消息到消息隊(duì)列中;
發(fā)送方不管是否在發(fā)送消息,接收方都可以從消息隊(duì)列中取到消息;
接收方在接收完消息之后,需要向消息隊(duì)列應(yīng)答成功。
2、Publish/Subscribe Messaging Domain(發(fā)布/訂閱通信模型)
訂閱/發(fā)布模式,同樣可以有著多個(gè)發(fā)送端與多個(gè)接收端,但是接收端與發(fā)送端存在時(shí)間上的依賴,就是如果發(fā)送端發(fā)送消息的時(shí)候,接收端并沒有監(jiān)聽消息,那么ActiveMQ將不會(huì)保存消息,將會(huì)認(rèn)為消息已經(jīng)發(fā)送,換一種說法,就是發(fā)送端發(fā)送消息的時(shí)候,接收端不在線,是接收不到消息的,哪怕以后監(jiān)聽消息,同樣也是接收不到的。這個(gè)模式還有一個(gè)特點(diǎn),那就是,發(fā)送端發(fā)送的消息,將會(huì)被所有的接收端給接收到,不類似點(diǎn)對(duì)點(diǎn),一條消息只會(huì)被一個(gè)接收端給接收到。
在發(fā)布/訂閱消息模型中,發(fā)布者發(fā)布一個(gè)消息,該消息通過topic傳遞給所有的客戶端。該模式下,發(fā)布者與訂閱者都是匿名的,即發(fā)布者與訂閱者都不知道對(duì)方是誰。并且可以動(dòng)態(tài)的發(fā)布與訂閱Topic。Topic主要用于保存和傳遞消息,且會(huì)一直保存消息直到消息被傳遞給客戶端。
(1)模式圖:
(2)特點(diǎn):
一個(gè)消息可以傳遞個(gè)多個(gè)訂閱者(即:一個(gè)消息可以有多個(gè)接收方);
發(fā)布者與訂閱者具有時(shí)間約束,針對(duì)某個(gè)主題(Topic)的訂閱者,它必須創(chuàng)建一個(gè)訂閱者之后,才能消費(fèi)發(fā)布者的消息,而且為了消費(fèi)消息,訂閱者必須保持運(yùn)行的狀態(tài);
為了緩和這樣嚴(yán)格的時(shí)間相關(guān)性,JMS允許訂閱者創(chuàng)建一個(gè)可持久化的訂閱。這樣,即使訂閱者沒有被激活(運(yùn)行),它也能接收到發(fā)布者的消息。
五、JMS接收消息
在JMS中,消息的接收可以使用以下兩種方式:
(1)同步
使用同步方式接收消息的話,消息訂閱者調(diào)用receive()方法。在receive()中,消息未到達(dá)或在到達(dá)指定時(shí)間之前,方法會(huì)阻塞,直到消息可用。
(2)異步
使用異步方式接收消息的話,消息訂閱者需注冊(cè)一個(gè)消息監(jiān)聽者,類似于事件監(jiān)聽器,只要消息到達(dá),JMS服務(wù)提供者會(huì)通過調(diào)用監(jiān)聽器的onMessage()遞送消息。
六、JMS編程模型
(1)管理對(duì)象(Administered objects)-連接工廠(Connection Factories)和目的地(Destination)
(2)連接對(duì)象(Connections)
(3)會(huì)話(Sessions)
(4)消息生產(chǎn)者(Message Producers)
(5)消息消費(fèi)者(Message Consumers)
(6)消息監(jiān)聽者(Message Listeners)
(1)JMS管理對(duì)象
管理對(duì)象(Administered objects)是預(yù)先配置的JMS對(duì)象,由系統(tǒng)管理員為使用JMS的客戶端創(chuàng)建,主要有兩個(gè)被管理的對(duì)象:
連接工廠(ConnectionFactory)目的地(Destination)
這兩個(gè)管理對(duì)象由JMS系統(tǒng)管理員通過使用Application Server管理控制臺(tái)創(chuàng)建,存儲(chǔ)在應(yīng)用程序服務(wù)器的JNDI名字空間或JNDI注冊(cè)表。
① 連接工廠(ConnectionFactory)
創(chuàng)建Connection對(duì)象的工廠,針對(duì)兩種不同的jms消息模型,分別有QueueConnectionFactory和TopicConnectionFactory兩種。可以通過JNDI來查找ConnectionFactory對(duì)象??蛻舳耸褂靡粋€(gè)連接工廠對(duì)象連接到JMS服務(wù)提供者,它創(chuàng)建了JMS服務(wù)提供者和客戶端之間的連接。JMS客戶端(如發(fā)送者或接受者)會(huì)在JNDI名字空間中搜索并獲取該連接。使用該連接,客戶端能夠與目的地通訊,往隊(duì)列或話題發(fā)送/接收消息。讓我們用一個(gè)例子來理解如何發(fā)送消息:
QueueConnectionFactory queueConnFactory = (QueueConnectionFactory) initialCtx.lookup ("primaryQCF");
Queue purchaseQueue = (Queue) initialCtx.lookup ("Purchase_Queue");
Queue returnQueue = (Queue) initialCtx.lookup ("Return_Queue");
② 目的地(Destination)
目的地指明消息被發(fā)送的目的地以及客戶端接收消息的來源。JMS使用兩種目的地,隊(duì)列和話題。如下代碼指定了一個(gè)隊(duì)列和話題。
創(chuàng)建一個(gè)隊(duì)列Session
QueueSession ses = con.createQueueSession (false, Session.AUTO_ACKNOWLEDGE); //get the Queue object
Queue t = (Queue) ctx.lookup ("myQueue"); //create QueueReceiver
QueueReceiver receiver = ses.createReceiver(t);
創(chuàng)建一個(gè)話題
TopicSession ses = con.createTopicSession (false, Session.AUTO_ACKNOWLEDGE); // get the Topic object
Topic t = (Topic) ctx.lookup ("myTopic"); //create TopicSubscriber
TopicSubscriber receiver = ses.createSubscriber(t);
(2)JMS連接對(duì)象(Connection)
Connection表示在客戶端和JMS系統(tǒng)之間建立的連接(對(duì)TCP/IP socket的包裝)。Connection可以產(chǎn)生一個(gè)或多個(gè)Session。跟ConnectionFactory一樣,Connection也有兩種類型:QueueConnection和TopicConnection。
連接對(duì)象封裝了與JMS提供者之間的虛擬連接,如果我們有一個(gè)ConnectionFactory對(duì)象,可以使用它來創(chuàng)建一個(gè)連接。
Connection connection = connectionFactory.createConnection();
創(chuàng)建完連接后,需要在程序使用結(jié)束后關(guān)閉它:connection.close();
(3)JMS會(huì)話(Session)
Session 是我們對(duì)消息進(jìn)行操作的接口,可以通過session創(chuàng)建生產(chǎn)者、消費(fèi)者、消息等。Session 提供了事務(wù)的功能,如果需要使用session發(fā)送/接收多個(gè)消息時(shí),可以將這些發(fā)送/接收動(dòng)作放到一個(gè)事務(wù)中。
我們可以在連接創(chuàng)建完成之后創(chuàng)建session:
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
這里面提供了參數(shù)兩個(gè)參數(shù),第一個(gè)參數(shù)是是否支持事務(wù),第二個(gè)是事務(wù)的類型。
(4)JMS消息生產(chǎn)者(Producer)
消息生產(chǎn)者由Session創(chuàng)建,用于往目的地發(fā)送消息。生產(chǎn)者實(shí)現(xiàn)MessageProducer接口,我們可以為目的地、隊(duì)列或話題創(chuàng)建生產(chǎn)者。
MessageProducer producer = session.createProducer(dest);
MessageProducer producer = session.createProducer(queue);
MessageProducer producer = session.createProducer(topic);
創(chuàng)建完消息生產(chǎn)者后,可以使用send方法發(fā)送消息:
(5)JMS消息消費(fèi)者(Consumer)
消息消費(fèi)者由Session創(chuàng)建,用于接收被發(fā)送到Destination的消息。消費(fèi)者實(shí)現(xiàn)MessageConsumer接口,我們可以為目的地、隊(duì)列或話題創(chuàng)建消費(fèi)者。
MessageConsumer consumer = session.createConsumer(dest);MessageConsumer consumer = session.createConsumer(queue);MessageConsumer consumer = session.createConsumer(topic);
(6)JMS消息監(jiān)聽器
JMS消息監(jiān)聽器是消息的默認(rèn)事件處理者,他實(shí)現(xiàn)了MessageListener接口,該接口包含一個(gè)onMessage方法,在該方法中需要定義消息達(dá)到后的具體動(dòng)作。通過調(diào)用setMessageListener方法,我們給指定的消費(fèi)者定義了消息監(jiān)聽器。
Listener myListener = new Listener();consumer.setMessageListener(myListener);
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
springboot配置ssl后啟動(dòng)一直是端口被占用的解決
這篇文章主要介紹了springboot配置ssl后啟動(dòng)一直是端口被占用的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08springboot yml配置文件使用@project.xxxx@啟動(dòng)報(bào)錯(cuò)Do not
這篇文章主要介紹了springboot yml配置文件使用@project.xxxx@啟動(dòng)報(bào)錯(cuò)Do not use @ for indentation問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07Spring?Security短信驗(yàn)證碼實(shí)現(xiàn)詳解
本文主要介紹了Spring?Security短信驗(yàn)證碼的實(shí)現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-11-11springcloud安裝rabbitmq并配置延遲隊(duì)列插件的過程詳解
本期主要講解如何利用docker快速安裝rabbitmq并且配置延遲隊(duì)列插件,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05SpringBoot執(zhí)行有返回值的異步任務(wù)問題
這篇文章主要介紹了SpringBoot執(zhí)行有返回值的異步任務(wù)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07springboot日期轉(zhuǎn)換器實(shí)現(xiàn)實(shí)例解析
這篇文章主要介紹了springboot日期轉(zhuǎn)換器實(shí)現(xiàn)實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12