1 基本信息
摘要:本篇爲JMS的學習筆記, 教你一步一步使用JMS,並提供一個能運行的完整的案例,可以使人達到快速入門的目的。
2 正文
JMS(Java Message Service),是Java消息服務,通過JMS,可以在Java對象之間發送消息。JMS消息支持點對點之間的消息發送,也支持主題/訂閱方式的消息發送。
/** 注: 本筆記中的代碼針在jboss4.0.5下運行通過 */
消息服務由客戶和消息代理組成。每位客戶都連接到消息服務,客戶可以創建消息、發送消息、接收消息、閱讀消息。消息服務可以將接收到的消息轉發給其他的客戶。
消息服務的關鍵特點:客戶只需要最少的信息就可以其他客戶通信(需知道其他客戶的提供的服務、服務所需信息和客戶地址)。
消息服務使用轉發-存儲結構以提交異步信息。
JMS包含5個元素:
• 提供者: 負責管理消息服務的消息代理
• 客戶: java編寫的,利用提供者進行通信的應用程序和組件
• 消息: 在客戶之間傳輸的對象
• 管理的對象: 傳輸中使用的jms對象,分兩種,目標工廠(Destination Factory)對象和連接對象,用於連接消息服務,處理髮送者和接收者之間的傳輸。
• 本機客戶: 是在引入jms之前構建的應用程序,它們是採用另外一種消息系統的本機客戶API
在引入jms之前,客戶是用 點對點 和 訂閱/發佈結構
jms的5個元素:
• 管理對象: 連接工廠(Connection Factory)對象和會話對象;連接工廠對象用於創建會話對象;會話對象用於創建發送者和接收者
ConnectionFactory(QueueConnectionFactory,TopicConnectionFactoy,XAQueueConnectionFactory,XATopicConnectionFactoy)
Connection(QueueConnection,TopicConnection,XAQueueConnection,XATopicConnection)
• 會話: Session
• 消息生成者: MessageProducer(QueueSender, TopicPublisher)
• 消息使用者: MessageConsumer(QueueReciever,TopicSubscriber)
• 消息: Message
jms使用步驟:
1 訪問連接工廠:
- InitialContext ctx=new InitialContext();
- TopicConnectionFactory tcf=(TopicConnectionFactory) ctx.lookup("TopicConnectionFactory");
- QueueConnectionFactory qcf=(QueueConnectionFactory) ctx.lookup("QueueConnectionFactory");
2 訪問目標工廠:創建Queue對象/Topic對象
- Topic mt=(Topic) ctx.lookup("topic/testTopic");
- Queue mq=(Queue) ctx.lookup("queue/A");
3 創建連接,分兩種:隊列連接和主題連接
- TopicConnection tc=tcf.createTopicConnection();
- QueueConnection qc=qcf.createQueueConnection();
4. 開始接收:也可以放在後面進行
- tc.start(); //以後需要調用 tc.close(); 關閉連接
- qc.start(); //以後需要調用 qc.close(); 關閉連接
5. 創建會話:會話用於創建消息生產者、消息消費者、消息。
- QueueSession qSession=qc.createQueueSession(true,0);
- TopicSession tSession=tc.createTopicSession(false,Session.AUTO_ACKNOWLEDGE);
參數1:是否使用事務
參數2:消息確認模式
當消息發送者向消息提供者(即消息代理)發送消息時,消息發送者等待消息代理的確認,沒有迴應則拋出異常,消息發送程序負責處理這個錯誤。
注:消息代理確認只是確認收到了消息,而不是確認消息提交給了消息接收者。
消息確認模式:JMS使用確認協議以保證消息的發送,使用了3種確認模式
AUTO_ACKNOWLEDGE : 指定消息提供者在每次收到消息時自動發送確認。消息只向目標發送一次,但傳輸過程中可能因爲錯誤而丟失消息。
CLIENT_ACKNOWLEDGE : 由消息接收者確認收到消息,通過調用消息的acknowledge()方法(會通知消息提供者收到了消息)
DUPS_OK_ACKNOWLEDGE : 指定消息提供者在消息接收者沒有確認發送時重新發送消息(這種確認模式不在乎接收者收到重複的消息)。如
消息提供者在試圖向非持久化的消息接收者發送消息失敗時,消息會丟失。在向持久化消息接收者發送消息時,會等待消息接收者確認,未收到確認,則重新發送 消息(消息提供者需要設置JMSRedilivered=true,消息接收者需要調用Message對象的getJMSRedelivered()方法 確認JMSRedilivered標記是否爲true)
CLIENT_ACKNOWLEDGE 確認收到消息代碼:
- public void onMessage(Message msg){
- try{
- //Process incoming messages
- msg.acknowledge();
- }catch(Exception e){
- //handle error
- }
- }
消息事務: 包含一組消息,要麼全部發送,要不全部都不發送給消息提供者。
消息提供者緩存消息,如果消息發送者有一個消息發送失敗,則調用session.rollback()方法,則消息提供者會放棄前面發送成功的消息;如果全部發送成功,調用session.commit()方法,將消息全部發送給消息接收者。
6 創建消息生產者:
- QueueSender qSender=qSession.createSender(mq);
- TopicPublisher tPublisher=tSession.createPublisher(mt);
- qSender.send(msg);
- tPublisher.publish(msg);
7 創建消息使用者:在接收任何消息之前,客戶必須註冊到JMS提供者表明希望接收消息。註冊後,JMS提供者就負責向客戶發送消息。
- QueueReceiver qr=qSession.createReceiver(mq);
- qc.start();
- Message msg1=qr.receive();
- TopicSubscriber tSubscriber=tSession.createSubscriber(mt);
- tc.start();
- Message msg2=tSubscriber.receive(1500); //每隔1500毫秒從主題接收一次
8 創建消息監聽器(實現MessageListener接口onMessage()方法):消息使用者通過消息監聽器異步接收消息。
- QueueListener qListener=new QueueListener(); //QueueListener爲自定義類,實現MessageListener接口
- qr.setMessageListener(qListener);
- TopicListener tListener=new TopicListener(); //TopicListener爲自定義類,實現MessageListener接口
- tSubscriber.setMessageListener(tListener);
9 消息(Message):三部分,頭(必須的),屬性和正文 (二者爲可選)。
消息頭讀寫方法: getXXX(),setXXX()
XXX是字段的名字,許多消息字段是由send()和publish()方法自動設置的,其他自動則由客戶或者JMS程序設置。
1) 由send() or publish()設置的:
- JMSDestination
- JMSDeliveryMode
- JMSExpiration
- JMSPriority
- JMSMessageID
- JMSTimestamp
- JMSCorrelationID: 關聯消息ID
- JMSReplyTo
- JMSType
- JMSRedelivered
2)由客戶設置的:
- JMSCorrelationID: 關聯消息ID
- JMSReplyTo
- JMSType
3) 由jms提供者設置的:
## 屬性讀寫方法: getXXX(name), setXXX(name,value)
消息正文:包含消息,JMS支持6種消息格式,稱爲消息類型
TextMessage: 有文本組成的String對象
MapMessage: 可以是按順序或隨機訪問的 key-value對,key爲String, value爲primitive
BytesMessage: 字節信息(如存放圖像)
StreamMessage: 包含順序讀取值的流
ObjectMessage: 可以序列化的java對象
Message: 無消息正文時可以使用
創建TextMessage消息:
- TextMessage msg=qSession.createTextMessage();
- msg.setText("myMessage");
- qSender.send(msg);
讀取TextMessage消息:
- Message msg=qr.receive();
- if(msg instanceof TextMessage){
- TextMessage txtMsg=(TextMessage) msg;
- System.out.println("Imcoming message: "+txtMsg.getText());
- }else{
- //handle error
- }
10. 消息選擇器:消息使用者使用消息選擇器(message selector)選擇收到的消息。消息選擇器使用條件表達式(符合WHERE子句的SQL-92標準)作爲選擇條件。
創建並使用消息選擇器:
- String criteria="Customer='1234'";
- TopicSubscriber tSubscriber=ts.createSubscriber(myTopic,criteria,false); //只從主題中接收Customer='1234'的消息
### 向隊列發送消息
- SendToQueue.java:
- package test.jms;
- import java.util.Properties;
- import javax.jms.*;
- import javax.naming.*;
- public class SendToQueue {
- public static void main(String[] args) {
- final int msgCount;
- if( (args.length<1) || ( args.length>2)) {
- System.out.println("Usage: java test.jms.SendToQueue queueName [sendCount]");
- System.exit(1);
- }
- String qName=new String(args[0]);
- if(args.length==2) {
- msgCount=(new Integer(args[1]).intValue());
- }else {
- msgCount=1;
- }
- QueueConnection qc=null;
- try {
- Properties p = new Properties();
- p.setProperty(Context.INITIAL_CONTEXT_FACTORY,
- "org.jnp.interfaces.NamingContextFactory");
- p.setProperty(Context.PROVIDER_URL, "localhost:1099");
- InitialContext jc=new InitialContext(p);
- QueueConnectionFactory qcf=(QueueConnectionFactory)jc.lookup("QueueConnectionFactory");
- Queue q=(Queue)jc.lookup(qName);
- qc=qcf.createQueueConnection();
- QueueSession qs=qc.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
- QueueSender qSender=qs.createSender(q);
- TextMessage msg=qs.createTextMessage();
- for(int i=0;i<msgCount;i++) {
- msg.setText("Welcome number "+(i+1));
- qSender.send(msg);
- System.out.println("Send Message To "+qName+" : "+msg.getText()+"\n");
- }
- qSender.close();
- qs.close();
- }catch(Exception e) {
- e.printStackTrace();
- }finally {
- if(qc!=null) {
- try {
- qc.close();
- }catch(JMSException e) {}
- }
- }
- }
- }
運行: java test.jms.SendToQueue queue/A 10
### 從隊列接收消息