在上一篇《ActiveMQ入門系列一:認識並安裝ActiveMQ(Windows下)》中,大致介紹了ActiveMQ和一些概念,並下載、安裝、啟動他,還訪問了他的控制檯頁面。
這篇,就用程式碼例項說下如何實現訊息的生產和消費。
一、理論基礎
同RabbitMQ一樣,ActiveMQ中也是有兩種模式:
- 點對點模式(Point to Point,簡寫為PTP)
- 釋出/訂閱模式(Publish & Subscribe,簡寫為Pub & Sub)
通過上一篇我們知道了製造訊息的應用叫生產者(Producer),生產者在生產了訊息後會傳送訊息到目的地(Destination),到達消費和處理訊息的應用(也就是消費者Consumer)。這裡的兩種模式就通過對應不同的訊息目的地(Destination)來實現,PTP對應Queue(佇列)、Pub&Sub對應Topic(主題)。
今天就詳細介紹下PTP和Queue,下一篇介紹Pub & Sub和Topic。
在PTP模式的示意圖:
- 訊息生產者生產訊息傳送到queue中,然後訊息消費者從queue中取出並且消費訊息。
- 訊息被消費以後,queue中不再有儲存,所以訊息消費者不可能消費到已經被消費的訊息。
- Queue支援存在多個消費者,但是對一個訊息而言,只會有一個消費者可以消費、其它的則不能消費此訊息了。
- 當消費者不存在時,訊息會一直儲存,直到有消費消費。
在PTP中,程式碼實現有兩種方式:消費者主動消費和消費者監聽消費,下面就分別說下。
二、消費者主動消費
主動消費是最基本也是最簡單的消費方式,先上程式碼:
- 建立maven工程並引入依賴
<dependency> <groupId>org.apache.activemq</groupId> <artifactId>activemq-core</artifactId> <version>5.7.0</version> </dependency>
- 實現生產者
package com.sam.ptp;
import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; /** * @author JAVA開發老菜鳥 * */ public class Producer { public static final String QUEUE_NAME = "ptp-demo";//佇列名 public void producer(String message) throws JMSException { ConnectionFactory factory = null; Connection connection = null; Session session = null; MessageProducer producer = null; try { /** * 1.建立連線工廠 * 建立工廠,構造方法有三個引數:分別是使用者名稱、密碼、連線地址 * 無參構造:有預設的連線地址,localhost * 一個引數:無驗證模式,無使用者的認證 * 三個引數:有認證和連線地址,我這裡使用三個引數的構造方法 */ factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616"); /** * 2.建立連線,有兩個方法(我這裡使用無引數的) * 無引數 * 有引數:使用者名稱、密碼; */ connection = factory.createConnection(); /** * 3.啟動連線 * 生產者可以不用呼叫start()方法啟動,因為在傳送訊息的時候回進行檢查 * 如果未啟動連線,會自動啟動。 * 如果有特殊配置,需要配置完成後再啟動連線 */ connection.start(); /** * 4.用連線建立會話 * 有兩個引數:是否需要事務、訊息確認機制 * 如果支援事務,對於生產者來說第二個引數就無效了,這個時候第二個引數建議傳入Session.SESSION_TRANSACTED * 如果不支援事務,第二個引數有效且必須傳遞 * * AUTO_ACKNOWLEDGE:自動確認,訊息處理後自動確認(商業開發不推薦) * CLIENT_ACKNOWLEDGE:客戶端手動確認,消費者處理後必須手動確認 * DUPS_OK_ACKNOWLEDGE:有副本的客戶端手動確認,訊息可以多次處理(不建議) */ session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); /** * 5.用會話建立目的地(佇列)、生產者、訊息 * 佇列名是佇列的唯一標記 * 建立生產者的時候可以指定目的地,也可以在傳送訊息的時候再指定 */ Destination destination = session.createQueue(QUEUE_NAME); producer = session.createProducer(destination); TextMessage textMessage = session.createTextMessage(message); /** * 6.生產者傳送訊息到目的地 */ producer.send(textMessage); System.out.println("訊息傳送成功"); } catch(Exception ex){ throw ex; } finally { /** * 7.釋放資源 */ if(producer != null){ producer.close(); } if(session != null){ session.close(); } if(connection != null){ connection.close(); } } } public static void main(String[] args){ Producer producer = new Producer(); try{ producer.producer("hello, activemq"); } catch (Exception ex){ ex.printStackTrace(); } } } - 實現消費者
package com.sam.ptp; import org.apache.activemq.ActiveMQConnectionFactory; import javax.jms.*; /** * @author JAVA開發老菜鳥 * * 主動消費 */ public class Consumer { public String consumer() throws JMSException { ConnectionFactory factory = null; Connection connection = null; Session session = null; MessageConsumer consumer = null; try { factory = new ActiveMQConnectionFactory("admin","admin","tcp://localhost:61616"); connection = factory.createConnection(); /** * 消費者必須啟動連線,否則無法消費 */ connection.start(); session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Destination destination = session.createQueue(Producer.QUEUE_NAME); consumer = session.createConsumer(destination); /** * 獲取佇列訊息 */ Message message = consumer.receive(); String text = ((TextMessage) message).getText(); return text; } catch(Exception ex){ throw ex; } finally { /** * 7.釋放資源 */ if(consumer != null){ consumer.close(); } if(session != null){ session.close(); } if(connection != null){ connection.close(); } } } public static void main(String[] args){ Consumer consumer = new Consumer(); try{ String message = consumer.consumer(); System.out.println("訊息消費成功:" + message); } catch (Exception ex){ ex.printStackTrace(); } } }
好,這樣程式碼就寫好了,我們來測試下。
1.先執行生產者,我發現報錯了。。。
好吧,原來是我這次沒有啟動ActiveMQ,被自己蠢哭了。。。
啟動ActiveMQ之後,再執行生產者,成功了。
去看下控制檯頁面的變化,佇列裡面多了個“ptp-demo”佇列,這個就是我們生產者程式碼裡面的佇列名,並且能看到該佇列的基本情況:
從左到右依次為,有待消費的訊息1條、消費者0個、已經傳送的訊息1條、已經消費的訊息0條
2.接下來執行消費者,成功
再去看下控制檯頁面,發現佇列資訊變了,從左到右依次為:有待消費的訊息0條、消費者0個、已經傳送的訊息1條、已經消費的訊息1條
也就是說,訊息真的被消費了!
程式碼寫完了,也按照預期執行完了,我們現在再回過頭來分析下消費者的程式碼,會發現他在consumer.receive()之後不會再消費其他訊息了,即便後面再有訊息被生產出來也不會再消費。也就是說只能在執行後消費一次訊息,這個就是主動消費。
如果想要迴圈消費多次產生的訊息的話,怎麼辦呢?請用下面的監聽消費
三、消費者監聽消費
還是先上程式碼,程式碼結構同主動消費類似,有細微差別,具體程式碼不貼了,可以到我的GitHub或碼雲上獲取原始碼
- 首先為了區分,我把佇列名改了
public static final String QUEUE_NAME = "ptp-listener-demo";//佇列名
- 生產者和消費者的訊息確認方式都改成了客戶端手動確認,不再自動確認,手動確認有個好處就是可以防止訊息沒有被正常消費而丟失,這個同RabbitMQ機制一樣
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
- 生產者生產訊息的時候,為了方便我改成了一次性傳送10條
/** * 6.建立訊息並且生產者傳送訊息到目的地 */ for(int num = 0; num < 10; num++){ TextMessage textMessage = session.createTextMessage(message + num); producer.send(textMessage); System.out.println("訊息傳送成功"+textMessage.getText()); }
- 關鍵點來了,在消費者上加了一個監聽器
/** * 註冊監聽器,佇列中的訊息變化會自動觸發監聽器,接收並自動處理訊息 * * 監聽器一旦註冊,永久有效,一直到程式關閉 * 監聽器可以註冊多個,相當於叢集 * activemq自動輪詢多個監聽器,實現並行處理 */ consumer.setMessageListener(new MessageListener() { @Override public void onMessage(Message message) { try { //需要手動確認訊息 message.acknowledge(); TextMessage om = (TextMessage) message; String data = om.getText(); System.out.println(data); } catch (JMSException e) { e.printStackTrace(); } } });
執行生產者:
執行消費者,訊息全部被消費了:
再執行2遍生產者,訊息同樣都被消費了。
控制檯頁面多了個佇列,由於監聽中的消費者沒有關閉,因此這裡能看到消費者數量為1,我執行了三遍生產者,因此訊息有30條。
還沒完,繼續...
我們這次先啟動2個消費者,然後啟動生產者
兩個生產者分別消費了訊息0,2,4,6,8和1,3,5,7,9
也就是說兩個消費者都監聽到了訊息,並且activemq自動輪詢兩個監聽器傳送訊息。
好,到這裡,ActiveMQ的點對點模式就介紹完了。下一篇介紹釋出訂閱模式,敬請期待