一垂寥、JMS是個什么
- JMS即Java消息服務(wù)(Java Message Service)應(yīng)用程序接口牌捷,是一個Java平臺中關(guān)于面向消息中間件(MOM)的API亲桥,用于在兩個應(yīng)用程序之間柑晒,或分布式系統(tǒng)中發(fā)送消息,進行異步通信囚衔。Java消息服務(wù)是一個與具體平臺無關(guān)的API铝穷,絕大多數(shù)MOM提供商都對JMS提供支持。
2佳魔、JMS是一組消息服務(wù)的api即接口規(guī)范
二、為什么要學(xué)習(xí)晦炊,使用JMS
1鞠鲜、在JAVA中,如果兩個應(yīng)用程序之間對各自都不了解断国,甚至這兩個程序可能部署在不同的大洲上贤姆,那么它們之間如何發(fā)送消息呢?舉個例子稳衬,一個應(yīng)用程序A部署在印度霞捡,另一個應(yīng)用程序部署在美國,然后每當(dāng)A觸發(fā)某件事后薄疚,B想從A獲取一些更新信息碧信。當(dāng)然,也有可能不止一個B對A的更新信息感興趣街夭,可能會有N個類似B的應(yīng)用程序想從A中獲取更新的信息砰碴。在這種情況下,JAVA提供了最佳的解決方案-JMS板丽,完美解決了上面討論的問題呈枉。JMS同樣適用于基于事件的應(yīng)用程序,如聊天服務(wù)埃碱,它需要一種發(fā)布事件機制向所有與服務(wù)器連接的客戶端發(fā)送消息猖辫。JMS與RMI不同,發(fā)送消息的時候砚殿,接收者不需要在線啃憎。服務(wù)器發(fā)送了消息,然后就不管了瓮具;等到客戶端上線的時候荧飞,能保證接收到服務(wù)器發(fā)送的消息。這是一個很強大的解決方案名党,能處理當(dāng)今世界很多普遍問題叹阔。
三、JMS有什么優(yōu)勢
1传睹、異步:JMS天生就是異步的耳幢,客戶端獲取消息的時候,不需要主動發(fā)送請求,消息會自動發(fā)送給可用的客戶端睛藻。
2启上、可靠:JMS保證消息只會遞送一次。大家都遇到過重復(fù)創(chuàng)建消息問題店印,而JMS能幫你避免該問題冈在,只是避免而不是杜絕,所以在一些糟糕的環(huán)境下還是有可能會出現(xiàn)重復(fù)按摘。
四包券、JMS消息傳送模型
在JMS API出現(xiàn)之前,大部分產(chǎn)品使用“點對點”和“發(fā)布/訂閱”中的任一方式來進行消息通訊炫贤。JMS定義了這兩種消息發(fā)送模型的規(guī)范溅固,它們相互獨立。任何JMS的提供者可以實現(xiàn)其中的一種或兩種模型兰珍,這是它們自己的選擇侍郭。JMS規(guī)范提供了通用接口保證我們基于JMS API編寫的程序適用于任何一種模型。讓我們更加詳細(xì)的看下這兩種消息傳送模型;
1掠河、點對點消息傳送模型(P2P)
在點對點消息傳送模型中亮元,應(yīng)用程序由消息隊列,發(fā)送者口柳,接收者組成苹粟。每一個消息發(fā)送給一個特殊的消息隊列,該隊列保存了所有發(fā)送給它的消息(除了被接收者消費掉的和過期的消息)跃闹。
點對點消息模型有如下特性:
(1)嵌削、每個消息只有一個接受者(自己測試了一下,可以有多個接受者望艺,但是當(dāng)有多個接收者時苛秕,每個接收者只能獲取隨機的幾條信息)
(2)、消息發(fā)送者和消息接受者并沒有時間依賴性找默。
(3)艇劫、當(dāng)消息發(fā)送者發(fā)送消息的時候,無論接收者程序在不在運行惩激,都能獲取到消息店煞;
(4)、當(dāng)接收者收到消息的時候风钻,會發(fā)送確認(rèn)收到通知(acknowledgement)顷蟀。
(5)、點對點消息模型圖:
2骡技、發(fā)布/訂閱消息傳送模型
在發(fā)布/訂閱消息模型中鸣个,發(fā)布者發(fā)布一個消息羞反,該消息通過topic傳遞給所有的客戶端。在這種模型中囤萤,發(fā)布者和訂閱者彼此不知道對方昼窗,是匿名的且可以動態(tài)發(fā)布和訂閱topic。topic主要用于保存和傳遞消息涛舍,且會一直保存消息直到消息被傳遞給客戶端澄惊。
發(fā)布/訂閱消息模型有如下特性:
(1)、一個消息可以傳遞給多個訂閱者
(2)富雅、發(fā)布者和訂閱者有時間依賴性缤削,只有當(dāng)客戶端創(chuàng)建訂閱后才能接受消息,且訂閱者需一直保持活動狀態(tài)以接收消息吹榴。
(3)、為了緩和這樣嚴(yán)格的時間相關(guān)性滚婉,JMS允許訂閱者創(chuàng)建一個可持久化的訂閱图筹。這樣,即使訂閱者沒有被激活(運行)让腹,它也能接收到發(fā)布者的消息远剩。
(4)、發(fā)布/訂閱消息模型圖:
五骇窍、接收消息
在JMS中瓜晤,消息的接收可以使用以下兩種方式:
1、同步方式:使用同步方式接收消息的話腹纳,消息訂閱者調(diào)用receive()方法痢掠。在receive()中,消息未到達或在到達指定時間之前嘲恍,方法會阻塞足画,直到消息可用。
1)佃牛、 目的地是Queue
consumer = session.createConsumer(queue);
Message message = consumer.receive(); //同步方式接收
(2)淹辞、目的地是Destination
consumer = session.createConsumer(destination);
//同步方式接受信息,如果還沒有獲取到則會阻塞直到接收到信息
Message messages = consumer.receive();
2、異步方式:使用異步方式接收消息的話俘侠,消息訂閱者需注冊一個消息監(jiān)聽者象缀,類似于事件監(jiān)聽器,只要消息到達爷速,JMS服務(wù)提供者會通過調(diào)用監(jiān)聽器的onMessage()遞送消息央星。
異步方式接收是使用了監(jiān)聽方式:
consumer.setMessageListener(new MessageListener(){
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
String value = textMessage.getText();
System.out.println("value: "+value);
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
六、JMS編程接口
JMS應(yīng)用程序由如下基本模塊組成:
1遍希、管理對象(Administered objects)-連接工廠(Connection Factories)和目的地(Destination)
2等曼、連接對象(Connections)
3、會話(Sessions)
4、消息生產(chǎn)者(Message Producers)
5禁谦、消息消費者(Message Consumers)
6胁黑、消息監(jiān)聽者(Message Listeners)
7、JMS管理對象
管理對象(Administered objects)是預(yù)先配置的JMS對象州泊,由系統(tǒng)管理員為使用JMS的客戶端創(chuàng)建丧蘸,主要有兩個被管理的對象:
連接工廠(ConnectionFactory)
目的地(Destination)
這兩個管理對象由JMS系統(tǒng)管理員通過使用Application Server管理控制臺創(chuàng)建,存儲在應(yīng)用程序服務(wù)器的JNDI名字空間或JNDI注冊表遥皂。
8力喷、連接工廠(ConnectionFactory)
客戶端使用一個連接工廠對象連接到JMS服務(wù)提供者,它創(chuàng)建了JMS服務(wù)提供者和客戶端之間的連接演训。JMS客戶端(如發(fā)送者或接受者)會在JNDI名字空間中搜索并獲取該連接弟孟。使用該連接,客戶端能夠與目的地通訊样悟,往隊列或話題發(fā)送/接收消息拂募。讓我們用一個例子來理解如何發(fā)送消息:
9、目的地(Destination)
目的地指明消息被發(fā)送的目的地以及客戶端接收消息的來源窟她。JMS使用兩種目的地陈症,隊列和話題。
10震糖、JMS連接
連接對象封裝了與JMS提供者之間的虛擬連接录肯,如果我們有一個ConnectionFactory對象,可以使用它來創(chuàng)建一個連接吊说。
//鏈接工廠
ActiveMQConnectionFactory connectionFactory = null;
//鏈接對象
Connection connection = null;
connectionFactory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.1.111:61616");
connection = connectionFactory.createConnection();
connection.start();
11论咏、JMS 會話(Session)
Session是一個單線程上下文,用于生產(chǎn)和消費消息颁井,可以創(chuàng)建出消息生產(chǎn)者和消息消費者潘靖。
Session對象實現(xiàn)了Session接口,在創(chuàng)建完連接后蚤蔓,我們可以使用它創(chuàng)建Session卦溢。
//第一個參數(shù)是否開啟事務(wù) true開啟 ,false不開啟事務(wù),如果開啟記得手動提交
//參數(shù)二秀又,表示的是簽收模式单寂,一般使用的有自動簽收和客戶端自己確認(rèn)簽收
session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
12、JMS消息生產(chǎn)者
消息生產(chǎn)者由Session創(chuàng)建吐辙,用于往目的地發(fā)送消息宣决。生產(chǎn)者實現(xiàn)MessageProducer接口,我們可以為目的地昏苏、隊列或話題創(chuàng)建生產(chǎn)者尊沸;
1)威沫、隊列(Queue)
Queue queue = session.createQueue("test_queue");
//為隊列創(chuàng)建消息生產(chǎn)者
MessageProducer producer = session.createProducer(queue);
2)、目的地(Destination)
Destination destination = session.createQueue("test-queue");
//為目的地創(chuàng)建消息生產(chǎn)者
MessageProducer producer = session.createProducer(destination);
1)洼专、2)這兩種方式只能用在點對點消息模型
3)棒掠、話題(Topic)
//創(chuàng)建話題
Topic topic = session.createTopic("myTopic.messages");
//為話題創(chuàng)建消息生產(chǎn)者
MessageProducer producer = session.createProducer(topic);
3)、只能用在發(fā)布/訂閱消息模型上屁商。
創(chuàng)建完消息生產(chǎn)者后烟很,可以使用send方法發(fā)送消息:
//創(chuàng)建消息
TextMessage message = session.createTextMessage();
message.setText("測試隊列消息"+i);
//發(fā)送消息到目的地
producer.send(message);
13、JMS消息消費者
消息消費者由Session創(chuàng)建蜡镶,用于接收目的地發(fā)送的消息雾袱。消費者實現(xiàn)MessageConsumer接口,我們可以為目的地官还、隊列或話題創(chuàng)建消費者芹橡;
1)、隊列(Queue)
Queue queue = session.createQueue("test_queue"); //隊列(目的地望伦,消費者消費消息的地方)
MessageConsumer consumer = session.createConsumer(queue); //消息消費者
2)僻族、目的地(Destination)
//消息目的地
Destination destination = session.createQueue("test-queue");
//消息消費者
MessageConsumer consumer = session.createConsumer(destination);
3)、話題(Topic)
//創(chuàng)建話題
Topic topic = session.createTopic("myTopic.messages");
//為話題創(chuàng)建消費者
MessageConsumer consumer = session.createConsumer(topic);
14屡谐、JMS消息監(jiān)聽器(消息監(jiān)聽這種方式也就是消息的異步接收方式)
JMS消息監(jiān)聽器是消息的默認(rèn)事件處理者,他實現(xiàn)了MessageListener接口蝌数,該接口包含一個onMessage方法愕掏,在該方法中需要定義消息達到后的具體動作。通過調(diào)用setMessageListener方法我們給指定消費者定義了消息監(jiān)聽器.
1)顶伞、TextMessage
consumer.setMessageListener(new MessageListener(){
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
String value = textMessage.getText();
System.out.println("value: "+value);
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
2)饵撑、ObjectMessage
consumer.setMessageListener(new MessageListener(){
@Override
public void onMessage(Message message){
try {
TestBean tbean =(TestBean)((ObjectMessage)message).getObject();
System.out.println("tbean: "+tbean);
if(null != message) {
System.out.println("收到信息1: "+tbean.getName());
}
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
七、JMS消息結(jié)構(gòu)
JMS客戶端使用JMS消息與系統(tǒng)通訊唆貌,JMS消息雖然格式簡單但是非常靈活滑潘, JMS消息由三部分組成:
1、消息頭
JMS消息頭預(yù)定義了若干字段用于客戶端與JMS提供者之間識別和發(fā)送消息锨咙,預(yù)編譯頭如下:
– JMSDestination : 消息發(fā)送的目的地语卤,主要是指Queue和Topic,由session創(chuàng)建而由生產(chǎn)者的send方法設(shè)置.
– JMSDeliveryMode:傳送模式:有兩種即久模式和非持久模式酪刀。一條持久性的消息應(yīng)該被傳輸"一次僅僅一次"粹舵,這就意味著如果JMS提供者出現(xiàn)故障,該消息并不會丟失骂倘,它會在服務(wù)器恢復(fù)之后再次傳遞眼滤。一條非持久的消息最多會傳遞一次,這意味著服務(wù)器出現(xiàn)故障历涝,該消息將永遠(yuǎn)丟失诅需。由session穿件由消息生產(chǎn)者的send方法設(shè)置
– JMSMessageID:唯一識別每個消息的標(biāo)識漾唉,由JMS消息生產(chǎn)者產(chǎn)生。由send方法設(shè)置
– JMSTimestamp:一個JMS Provider在調(diào)用send()方法時自動設(shè)置堰塌,它是消息被發(fā)送和消費者實際接收的時間差赵刑。由客戶端設(shè)置
– JMSCorrelationID:用來連接到另外一個消息,典型的應(yīng)用是在回復(fù)消息中連接到原消息蔫仙。在大多數(shù)情況下料睛,JMSCorrelationID用于將一條消息標(biāo)記為對JMSMessageID標(biāo)示的上一條消息的應(yīng)答,不過摇邦,JMSCorrelationID可以是任何值恤煞,不僅僅是JMSMessageID。由客戶端設(shè)置
– JMSReplyTo:提供本消息回復(fù)消息的目的地址,由客戶端設(shè)置
– JMSRedelivered:如果一個客戶端收到一個設(shè)置了JMSRedelivered屬性的消息施籍,則表示可能客戶端曾經(jīng)在早些時候收到過該消息居扒,但并沒有簽收(acknowledged)。如果該消息被重新傳送丑慎,JMSRedelivered=true 否則 JMSRedelivered=flase 喜喂。由JMS Provider設(shè)置
– JMSType:消息類型的標(biāo)識符,由客戶端設(shè)置
– JMSExpiration:消息過期時間竿裂,等于Destination的send方法中的timeToLive值加上發(fā)送時刻的GMT的時間值玉吁。如果timeToLive值等于零,則JMSExpiration被設(shè)置為零腻异,表示該消息永不過期进副。如果發(fā)送后,在消息過期時間之后消息還沒有被發(fā)送到目的地悔常,則該消息被清除影斑。由send方法設(shè)置
– JMSPriority:消息優(yōu)先級,從0-9十個級別机打,0-4是普通消息矫户,5-9是加急消息。JMS不要求JMS Provider嚴(yán)格按照這十個優(yōu)先級發(fā)送消息残邀,但必須保證加急消息要先于普通消息到達皆辽,默認(rèn)是4級。由send方法設(shè)置
一個消息的消息頭有這些屬性芥挣,我們可以按照需要對這個消息的消息進行設(shè)計膳汪,在將這個消息使用消息生產(chǎn)者的send()方法發(fā)送到消息服務(wù)上。
2九秀、消息屬性
我們可以給消息設(shè)置自定義屬性遗嗽,這些屬性主要是提供給應(yīng)用程序的。對于實現(xiàn)消息過濾功能鼓蜒,消息屬性非常有用痹换,JMS API定義了一些標(biāo)準(zhǔn)屬性征字,JMS服務(wù)提供者可以選擇性的提供部分標(biāo)準(zhǔn)屬性。
3娇豫、消息體
在消息體中匙姜,JMS API定義了五種類型的消息格式,讓我們可以以不同的形式發(fā)送和接受消息冯痢,并提供了對已有消息格式的兼容氮昧。不同的消息類型如下:
Text message : javax.jms.TextMessage,表示一個文本對象浦楣。
Object message : javax.jms.ObjectMessage袖肥,表示一個JAVA對象。
Bytes message : javax.jms.BytesMessage振劳,表示字節(jié)數(shù)據(jù)椎组。
Stream message :javax.jms.StreamMessage,表示java原始值數(shù)據(jù)流历恐。
Map message : javax.jms.MapMessage寸癌,表示鍵值對。
參考文章:http://www.cnblogs.com/chenpi/p/5559349.html#_label6
http://howtodoinjava.com/jms/jms-java-message-service-tutorial/