訊息佇列ActiveMQ的使用詳解

SnailClimb發表於2018-04-23

通過上一篇文章 《訊息佇列深入解析》,我們已經訊息佇列是什麼、使用訊息佇列的好處以及常見訊息佇列的簡單介紹。

這一篇文章,主要帶大家詳細瞭解一下訊息佇列ActiveMQ的使用。

學習訊息佇列ActiveMQ的使用之前,我們先來搞清JMS

JMS

1. JMS基本概念

JMS(JAVA Message Service,java訊息服務)是java的訊息服務,JMS的客戶端之間可以通過JMS服務進行非同步的訊息傳輸。JMS(JAVA Message Service,java訊息服務)API是一個訊息服務的標準或者說是規範,允許應用程式元件基於JavaEE平臺建立、傳送、接收和讀取訊息。它使分散式通訊耦合度更低,訊息服務更加可靠以及非同步性。

2. JMS五種不同的訊息正文格式

JMS定義了五種不同的訊息正文格式,以及呼叫的訊息型別,允許你傳送並接收以一些不同形式的資料,提供現有訊息格式的一些級別的相容性。

  • StreamMessage -- Java原始值的資料流
  • MapMessage--一套名稱-值對
  • TextMessage--一個字串物件
  • ObjectMessage--一個序列化的 Java物件
  • BytesMessage--一個位元組的資料流

3.JMS兩種訊息模型

1 .點到點(P2P)模型

點到點(P2P)模型
使用佇列(Queue)作為訊息通訊載體;滿足生產者與消費者模式,一條訊息只能被一個消費者使用,未被消費的訊息在佇列中保留直到被消費或超時。比如:我們生產者傳送100條訊息的話,兩個消費者來消費一般情況下兩個消費者會按照訊息傳送的順序各自消費一半(也就是你一個我一個的消費。)後面我們會通過程式碼演示來驗證。

2. 釋出/訂閱(Pub/Sub)模型

釋出/訂閱(Pub/Sub)模型
釋出訂閱模型(Pub/Sub) 使用主題(Topic)作為訊息通訊載體,類似於廣播模式;釋出者釋出一條訊息,該訊息通過主題傳遞給所有的訂閱者,在一條訊息廣播之後才訂閱的使用者則是收不到該條訊息的

4.JMS編碼介面之間的關係

MS編碼介面之間的關係

  • ConnectionFactory:建立Connection物件的工廠,針對兩種不同的jms訊息模型,分別有QueueConnectionFactory和TopicConnectionFactory兩種。可以通過JNDI來查詢ConnectionFactory物件。

  • Connection:Connection表示在客戶端和JMS系統之間建立的連結(對TCP/IP socket的包裝)。Connection可以產生一個或多個Session。跟ConnectionFactory一樣,Connection也有兩種型別:QueueConnection和TopicConnection。

  • Session:Session是操作訊息的介面。可以通過session建立生產者、消費者、訊息等。Session提供了事務的功能。當需要使用session傳送/接收多個訊息時,可以將這些傳送/接收動作放到一個事務中。同樣,也分QueueSession和TopicSession。

  • MessageProducer:訊息生產者由Session建立,並用於將訊息傳送到Destination。同樣,訊息生產者分兩種型別:QueueSender和TopicPublisher。可以呼叫訊息生產者的方法(send或publish方法)傳送訊息。

  • MessageConsumer :訊息消費者由Session建立,用於接收被髮送到Destination的訊息。兩種型別:QueueReceiver和TopicSubscriber。可分別通過session的createReceiver(Queue)或createSubscriber(Topic)來建立。當然,也可以session的creatDurableSubscriber方法來建立持久化的訂閱者。

  • Destination:Destination的意思是訊息生產者的訊息傳送目標或者說訊息消費者的訊息來源。對於訊息生產者來說,它的Destination是某個佇列(Queue)或某個主題(Topic);對於訊息消費者來說,它的Destination也是某個佇列或主題(即訊息來源)。

  • MessageListener: 訊息監聽器。如果註冊了訊息監聽器,一旦訊息到達,將自動呼叫監聽器的onMessage方法。

參考:https://blog.csdn.net/shaobingj126/article/details/50585035

訊息佇列ActiveMQ

1.簡介

ActiveMQ 是Apache出品,最流行的,能力強勁的開源訊息匯流排。ActiveMQ 是一個完全支援JMS1.1和J2EE 1.4規範的 JMS Provider實現,儘管JMS規範出臺已經是很久的事情了,但是JMS在當今的J2EE應用中間仍然扮演著特殊的地位。

2.簡單使用

安裝過程很簡單這裡就不貼安裝過程了,可以自行google.

新增Maven依賴

    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-all</artifactId>
        <version>5.15.3</version>
    </dependency>
複製程式碼

2.1.測試點對點模型通訊

生產者傳送訊息測試方法:

	@Test
	public void testQueueProducer() throws Exception {

		// 1、建立一個連線工廠物件,需要指定服務的ip及埠。
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.155:61616");
		// 2、使用工廠物件建立一個Connection物件。
		Connection connection = connectionFactory.createConnection();
		// 3、開啟連線,呼叫Connection物件的start方法。
		connection.start();
		// 4、建立一個Session物件。
		// 第一個引數:是否開啟事務。如果true開啟事務,第二個引數無意義。一般不開啟事務false。
		// 第二個引數:應答模式。自動應答或者手動應答。一般自動應答。
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		// 5、使用Session物件建立一個Destination物件。兩種形式queue、topic,現在應該使用queue
		Queue queue = session.createQueue("test-queue");
		// 6、使用Session物件建立一個Producer物件。
		MessageProducer producer = session.createProducer(queue);
		// 7、建立一個Message物件,可以使用TextMessage。
		for (int i = 0; i < 50; i++) {
			TextMessage textMessage = session.createTextMessage("第"+i+ "一個ActiveMQ佇列目的地的訊息");
			// 8、傳送訊息
			producer.send(textMessage);
		}

		// 9、關閉資源
		producer.close();
		session.close();
		connection.close();
	}
複製程式碼

消費者消費訊息測試方法

	@Test
	public void testQueueConsumer() throws Exception {
		// 建立一個ConnectionFactory物件連線MQ伺服器
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.155:61616");
		// 建立一個連線物件
		Connection connection = connectionFactory.createConnection();
		// 開啟連線
		connection.start();
		// 使用Connection物件建立一個Session物件
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		// 建立一個Destination物件。queue物件
		Queue queue = session.createQueue("test-queue");
		// 使用Session物件建立一個消費者物件。
		MessageConsumer consumer = session.createConsumer(queue);
		// 接收訊息
		consumer.setMessageListener(new MessageListener() {

			@Override
			public void onMessage(Message message) {
				// 列印結果
				TextMessage textMessage = (TextMessage) message;
				String text;
				try {
					text = textMessage.getText();
					System.out.println("這是接收到的訊息:" + text);
				} catch (JMSException e) {
					e.printStackTrace();
				}

			}
		});
		// 等待接收訊息
		System.in.read();
		// 關閉資源
		consumer.close();
		session.close();
		connection.close();
	}

複製程式碼

我們開啟兩個消費者程式來監聽(執行兩次testQueueConsumer()方法)。

開啟兩個消費者程式來監聽
然後我們執行執行生產者測試方法傳送訊息.先傳送訊息還是先監聽訊息一般不會不影響。

效果如下:

兩個消費者各自消費一半訊息,而且還是按照訊息傳送到訊息佇列的順序,這也驗證了我們上面的說法。 第一個消費者

第一個消費者

第二個消費者

第二個消費者

2.2.測試釋出/訂閱(Pub/Sub)模型通訊

生產者傳送訊息測試方法:

	@Test
	public void testTopicProducer() throws Exception {
		// 1、建立一個連線工廠物件,需要指定服務的ip及埠。
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.155:61616");
		// 2、使用工廠物件建立一個Connection物件。
		Connection connection = connectionFactory.createConnection();
		// 3、開啟連線,呼叫Connection物件的start方法。
		connection.start();
		// 4、建立一個Session物件。
		// 第一個引數:是否開啟事務。如果true開啟事務,第二個引數無意義。一般不開啟事務false。
		// 第二個引數:應答模式。自動應答或者手動應答。一般自動應答。
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		// 5、使用Session物件建立一個Destination物件。兩種形式queue、topic,現在應該使用topic
		Topic topic = session.createTopic("test-topic");
		// 6、使用Session物件建立一個Producer物件。
		MessageProducer producer = session.createProducer(topic);
		// 7、建立一個Message物件,可以使用TextMessage。
		for (int i = 0; i < 50; i++) {
			TextMessage textMessage = session.createTextMessage("第"+i+ "一個ActiveMQ佇列目的地的訊息");
			// 8、傳送訊息
			producer.send(textMessage);
		}
		// 9、關閉資源
		producer.close();
		session.close();
		connection.close();
	}
複製程式碼

消費者消費訊息測試方法:

	@Test
	public void testTopicConsumer() throws Exception {
		// 建立一個ConnectionFactory物件連線MQ伺服器
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.155:61616");
		// 建立一個連線物件
		Connection connection = connectionFactory.createConnection();
		// 開啟連線
		connection.start();
		// 使用Connection物件建立一個Session物件
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		// 建立一個Destination物件。topic物件
		Topic topic = session.createTopic("test-topic");
		// 使用Session物件建立一個消費者物件。
		MessageConsumer consumer = session.createConsumer(topic);
		// 接收訊息
		consumer.setMessageListener(new MessageListener() {

			@Override
			public void onMessage(Message message) {
				// 列印結果
				TextMessage textMessage = (TextMessage) message;
				String text;
				try {
					text = textMessage.getText();
					System.out.println("這是接收到的訊息:" + text);
				} catch (JMSException e) {
					e.printStackTrace();
				}

			}
		});
		System.out.println("topic消費者啟動。。。。");
		// 等待接收訊息
		System.in.read();
		// 關閉資源
		consumer.close();
		session.close();
		connection.close();
	}
複製程式碼

先執行兩個消費者程式(提前訂閱,不然收不到傳送的訊息),然後執行生產者測試方法傳送訊息。

結果是: 兩個消費者程式都可以接收到生產者傳送過來的所有訊息,我這裡就不貼圖片了, 這樣驗證了我們上面的說法。

我們從上面程式碼就可以看出,點對點通訊和釋出訂閱通訊模式的區別就是建立生產者和消費者物件時提供的Destination物件不同,如果是點對點通訊建立的Destination物件是Queue,釋出訂閱通訊模式通訊則是Topic。

3.整合Spring使用

整合spring除了我們上面依賴的Jar包還要依賴

     <dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-jms</artifactId>
		<version>4.2.7.RELEASE</version>
	</dependency>
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-context-support</artifactId>
		<version>4.2.7.RELEASE</version>
     </dependency>

複製程式碼

比如我們在我們的系統中現在有兩個服務,第一個服務傳送訊息,第二個服務接收訊息,我們下面看看這是如何實現的。

傳送訊息

傳送訊息的配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

	<!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供 -->
	<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
		<property name="brokerURL" value="tcp://192.168.25.155:61616" />
	</bean>
	<!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->
	<bean id="connectionFactory"
		class="org.springframework.jms.connection.SingleConnectionFactory">
		<!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->
		<property name="targetConnectionFactory" ref="targetConnectionFactory" />
	</bean>
	<!-- 配置生產者 -->
	<!-- Spring提供的JMS工具類,它可以進行訊息傳送、接收等 -->
	<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
		<!-- 這個connectionFactory對應的是我們定義的Spring提供的那個ConnectionFactory物件 -->
		<property name="connectionFactory" ref="connectionFactory" />
	</bean>
	<!--這個是佇列目的地,點對點的 -->
	<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
		<constructor-arg>
			<value>spring-queue</value>
		</constructor-arg>
	</bean>
	<!--這個是主題目的地,一對多的 -->
	<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
		<constructor-arg value="topic" />
	</bean>
</beans>

複製程式碼

傳送訊息的測試方法:

@Test
	public void testSpringActiveMq() throws Exception {
		//初始化spring容器
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-activemq.xml");
		//從spring容器中獲得JmsTemplate物件
		JmsTemplate jmsTemplate = applicationContext.getBean(JmsTemplate.class);
		//從spring容器中取Destination物件
		Destination destination = (Destination) applicationContext.getBean("queueDestination");
		//使用JmsTemplate物件傳送訊息。
		jmsTemplate.send(destination, new MessageCreator() {
			
			@Override
			public Message createMessage(Session session) throws JMSException {
				//建立一個訊息物件並返回
				TextMessage textMessage = session.createTextMessage("spring activemq queue message");
				return textMessage;
			}
		});
	}
複製程式碼

我們上面直接ApplicationContext的getBean方法獲取的物件,實際在專案使用依賴注入即可。

接收訊息

建立一個MessageListener的實現類。

public class MyMessageListener implements MessageListener {

	@Override
	public void onMessage(Message message) {
		
		try {
			TextMessage textMessage = (TextMessage) message;
			//取訊息內容
			String text = textMessage.getText();
			System.out.println(text);
		} catch (JMSException e) {
			e.printStackTrace();
		}
	}

}
複製程式碼

接收訊息的配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

	<!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供 -->
	<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
		<property name="brokerURL" value="tcp://192.168.25.168:61616" />
	</bean>
	<!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->
	<bean id="connectionFactory"
		class="org.springframework.jms.connection.SingleConnectionFactory">
		<!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->
		<property name="targetConnectionFactory" ref="targetConnectionFactory" />
	</bean>
	<!--這個是佇列目的地,點對點的 -->
	<bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
		<constructor-arg>
			<value>spring-queue</value>
		</constructor-arg>
	</bean>
	<!--這個是主題目的地,一對多的 -->
	<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
		<constructor-arg value="topic" />
	</bean>
	<!-- 接收訊息 -->
	<!-- 配置監聽器 -->
	<bean id="myMessageListener" class="cn.e3mall.search.listener.MyMessageListener" />
	<!-- 訊息監聽容器 -->
	<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
		<property name="connectionFactory" ref="connectionFactory" />
		<property name="destination" ref="queueDestination" />
		<property name="messageListener" ref="myMessageListener" />
	</bean>
</beans>
複製程式碼

測試接收訊息的程式碼

@Test
	public void testQueueConsumer() throws Exception {
		//初始化spring容器
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-activemq.xml");
		//等待
		System.in.read();
	}

複製程式碼

歡迎關注我的微信公眾號:"Java面試通關手冊"(一個有溫度的微信公眾號,期待與你共同進步~~~堅持原創,分享美文,分享各種Java學習資源):

微信公眾號

相關文章