ActiveMQ 基礎知識

第一 消息中間件概述

1.1 什麼是消息中間件

面向消息的中間件(MessageOrlented MiddlewareMOM),發送者將消息發送給消息服務器,消息服務器將消感存放在若千隊列中,在合適的時候再將消息轉發給接收者。

重點:發送和接收是異步的,發送者無需等
待; 二者的生命週期未必相同: 發送消息的時候接收者不一定運行,接收消息的時候
發送者也不一定運行;一對多通信: 對於一個消息可以有多個接收者。

1.2 JMS介紹

1.2.1 什麼是JMS 

JMS是java的消息服務,JMS的客戶端之間可以通過JMS服務進行異步的消息傳輸。

1.2.2 消息模式

消息模式分爲兩種:Point-to-Point(P2P)模式,也稱點對點模式

                              Publish/Subscribe(Pub/Sub)模式,也稱發佈訂閱模式

1.2.2.1 P2P模式

P2P模式圖:

涉及相關概念:

1、消息隊列(Queue)
2、發送者(Sender)
3、接收者(Receiver)
4、每個消息都被髮送到一個特定的隊列,接收者從隊列中獲取消息。隊列保留着消息,直到他們被消費或超時。

P2P 的特點:

1.每個消息只有一個消費者(Consumer)(即一旦被消費,消息就不再在消息隊列中)
2.發送者和接收者之間在時間上沒有依賴性,也就是說當發送者發送了消息之後,不管接收者有沒有正在運行,它不會影響到消息被髮送到隊列
3.接收者在成功接收消息之後需向隊列應答成功

注意:如果你希望發送的每個消息都應該被成功處理的話,那麼你需要P2P模式

1.2.2.2 Pub/Sub模式

Pub/Sub模式圖解:

涉及相關概念:

1、主題(Topic)
2、發佈者(Publisher)
3、訂閱者(Subscriber)
4、發佈者將消息發送到Topic,系統將這些消息傳遞給多個訂閱者。

Pub/Sub模式特點

1、每個消息可以有多個消費者

注意:如果你希望發送的消息可以不被做任何處理、或者被一個消息者處理、或者可以被多個消費者處理的話,那麼可以採用Pub/Sub模型

1.2.3 消息的消費模式

JMS的訂閱者/接受者可以通過兩種方式來消費消息。 (同步和異步)

同步:訂閱者或接收者調用receive方法來接收消息,receive方法在能夠接收到消息之前(或超時之前)將一直阻塞 。

異步:訂閱者或接收者可以註冊爲一個消息監聽器。當消息到達之後,系統自動調用監聽器的onMessage方法。

 

第二 消息中間件的種類

RabbitMQ

是使用Erlang編寫的一個開源的消息隊列,本身支持很多的協議:AMQP,XMPP, SMTP, STOMP,也正是如此,使的它變的非常重量級,更適合於企業級的開發。同時實現了一個經紀人(Broker)構架,這意味着消息在發送給客戶端時先在中心隊列排隊。對路由(Routing),負載均衡(Load balance)或者數據持久化都有很好的支持。

Redis

是一個Key-Value的NoSQL數據庫,開發維護很活躍,雖然它是一個Key-Value數據庫存儲系統,但它本身支持MQ功能,所以完全可以當做一個輕量級的隊列服務來使用。對於RabbitMQ和Redis的入隊和出隊操作,各執行100萬次,每10萬次記錄一次執行時間。測試數據分爲128Bytes、512Bytes、1K和10K四個不同大小的數據。實驗表明:入隊時,當數據比較小時Redis的性能要高於RabbitMQ,而如果數據大小超過了10K,Redis則慢的無法忍受;出隊時,無論數據大小,Redis都表現出非常好的性能,而RabbitMQ的出隊性能則遠低於Redis。

 

入隊

出隊

 

128B

512B

1K

10K

128B

512B

1K

10K

Redis

16088

15961

17094

25

15955

20449

18098

9355

RabbitMQ

10627

9916

9370

2366

3219

3174

2982

1588

ZeroMQ

號稱最快的消息隊列系統,尤其針對大吞吐量的需求場景。ZMQ能夠實現RabbitMQ不擅長的高級/複雜的隊列,但是開發人員需要自己組合多種技術框架,技術上的複雜度是對這MQ能夠應用成功的挑戰。ZeroMQ具有一個獨特的非中間件的模式,你不需要安裝和運行一個消息服務器或中間件,因爲你的應用程序將扮演了這個服務角色。你只需要簡單的引用ZeroMQ程序庫,可以使用NuGet安裝,然後你就可以愉快的在應用程序之間發送消息了。但是ZeroMQ僅提供非持久性的隊列,也就是說如果down機,數據將會丟失。其中,Twitter的Storm中使用ZeroMQ作爲數據流的傳輸。

ActiveMQ

是Apache下的一個子項目。 類似於ZeroMQ,它能夠以代理人和點對點的技術實現隊列。同時類似於RabbitMQ,它少量代碼就可以高效地實現高級應用場景。RabbitMQ、ZeroMQ、ActiveMQ均支持常用的多種語言客戶端 C++、Java、.Net,、Python、 Php、 Ruby等。

Jafka/Kafka

Kafka是Apache下的一個子項目,是一個高性能跨語言分佈式Publish/Subscribe消息隊列系統,而Jafka是在Kafka之上孵化而來的,即Kafka的一個升級版。具有以下特性:快速持久化,可以在O(1)的系統開銷下進行消息持久化;高吞吐,在一臺普通的服務器上既可以達到10W/s的吞吐速率;完全的分佈式系統,Broker、Producer、Consumer都原生自動支持分佈式,自動實現複雜均衡;支持Hadoop數據並行加載,對於像Hadoop的一樣的日誌數據和離線分析系統,但又要求實時處理的限制,這是一個可行的解決方案。Kafka通過Hadoop的並行加載機制來統一了在線和離線的消息處理,這一點也是本課題所研究系統所看重的。Apache Kafka相對於ActiveMQ是一個非常輕量級的消息系統,除了性能非常好之外,還是一個工作良好的分佈式系統。

其他一些隊列列表HornetQ、Apache Qpid、Sparrow、Starling、Kestrel、Beanstalkd、Amazon SQS就不再一一分析。

 

第三 消息中間件ActiveMQ 的使用

3.1 Windows 安裝ActiveMQ

ActiveMQ部署其實很簡單,和所有Java一樣,要跑java程序就必須先安裝JDK並配置好環境變量,這個很簡單。

然後解壓下載的apache-activemq-5.15.13.zip壓縮包到一個目錄,得到解壓後的目錄結構如下:

進入bin目錄,發現有win32和win64兩個文件夾,這2個文件夾分別對應windows32位和windows64位操作系統的啓動腳本。

我的實驗環境是windows7,就進入win64目錄,會看到如下目錄結構。

其中activemq.bat便是啓動腳本,雙擊啓動。

ActiveMQ默認啓動到8161端口,啓動完了後在瀏覽器地址欄輸入:http://localhost:8161/admin要求輸入用戶名密碼,默認用戶名密碼爲admin、admin,這個用戶名密碼是在conf/users.properties中配置的。輸入用戶名密碼後便可看到如下圖的ActiveMQ控制檯界面了。

 

3.1 ActiveMQ 控制檯介紹(消息對列頁面)

Number Of Consumers  消費者 這個是消費者端的消費者數量 
Number Of Pending Messages 等待消費的消息 這個是當前未出隊列的數量。可以理解爲總接收數-總出隊列數 
Messages Enqueued 進入隊列的消息  進入隊列的總數量,包括出隊列的。 這個數量只增不減 
Messages Dequeued 出了隊列的消息  可以理解爲是消費這消費掉的數量 
這個要分兩種情況理解 
在queues裏它和進入隊列的總數量相等(因爲一個消息只會被成功消費一次),如果暫時不等是因爲消費者還沒來得及消費。 
在 topics裏 它因爲多消費者從而導致數量會比入隊列數高。 
簡單的理解上面的意思就是 
當有一個消息進入這個隊列時,等待消費的消息是1,進入隊列的消息是1。 
當消息消費後,等待消費的消息是0,進入隊列的消息是1,出隊列的消息是1. 
在來一條消息時,等待消費的消息是1,進入隊列的消息就是2. 


沒有消費者時  Pending Messages   和 入隊列數量一樣 
有消費者消費的時候 Pedding會減少 出隊列會增加 
到最後 就是 入隊列和出隊列的數量一樣多 
以此類推,進入隊列的消息和出隊列的消息是池子,等待消費的消息是水流。 

3.2 Java 實現ActiveMQ P2P模式:

pom.xml 文件添加如下依賴配置:

<!--activeMQ jar包 -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-all</artifactId>
			<version>5.15.13</version>
		</dependency>

ActiveMQ 之生產者

package com.zzg.activemq.p2p;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 消息生產者
 * 
 * @author Administrator
 *
 */
public class Producter {
	public static void main(String[] args) throws JMSException {
		// ConnectionFactory :連接工廠,JMS 用它創建連接
		ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
				ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
		// JMS 客戶端到JMS Provider 的連接
		Connection connection = connectionFactory.createConnection();
		connection.start();
		// Session: 一個發送或接收消息的線程
		Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
		// Destination :消息的目的地;消息發送給誰.
		// 獲取session注意參數值my-queue是Query的名字
		Destination destination = session.createQueue("my-queue");
		// MessageProducer:消息生產者
		MessageProducer producer = session.createProducer(destination);
		// 設置不持久化
		producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
		// 發送一條消息
		for (int i = 1; i <= 5; i++) {
			sendMsg(session, producer, i);
		}
		session.commit();
		connection.close();
	}
	
	/**
	 * 在指定的會話上,通過指定的消息生產者發出一條消息
	 * 
	 * @param session
	 *            消息會話
	 * @param producer
	 *            消息生產者
	 */
	public static void sendMsg(Session session, MessageProducer producer, int i) throws JMSException {
		// 創建一條文本消息
		TextMessage message = session.createTextMessage("Hello ActiveMQ!" + i);
		// 通過消息生產者發出消息
		producer.send(message);
	}
}

ActiveMQ 之消費者 

package com.zzg.activemq.p2p;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 消息消費者
 * @author Administrator
 *
 */
public class JmsReceiver {

	public static void main(String[] args) throws JMSException {
		// ConnectionFactory :連接工廠,JMS 用它創建連接
				ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
						ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
				// JMS 客戶端到JMS Provider 的連接
				Connection connection = connectionFactory.createConnection();
				connection.start();
				// Session: 一個發送或接收消息的線程
				Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
				// Destination :消息的目的地;消息發送給誰.
				// 獲取session注意參數值xingbo.xu-queue是一個服務器的queue,須在在ActiveMq的console配置
				Destination destination = session.createQueue("my-queue");
				// 消費者,消息接收者
				MessageConsumer consumer = session.createConsumer(destination);
				while (true) {
					TextMessage message = (TextMessage) consumer.receive();
					if (null != message) {
						System.out.println("收到消息:" + message.getText());
						session.commit();
					} else
						break;
				}
				session.close();
				connection.close();
	}

}

3.3 ActiveMQ 消息可靠機制

客戶端成功接收一條消息的標誌是一條消息被簽收,成功應答。

消息的簽收情形分兩種:

1、帶事務的session

   如果session帶有事務,並且事務成功提交,則消息被自動簽收。如果事務回滾,則消息會被再次傳送。

2、不帶事務的session

   不帶事務的session的簽收方式,取決於session的配置。

 Activemq支持一下三種模式:
   Session.AUTO_ACKNOWLEDGE  消息自動簽收

   Session.CLIENT_ACKNOWLEDGE  客戶端調用acknowledge方法手動簽收
   textMessage.acknowledge();//手動簽收

   Session.DUPS_OK_ACKNOWLEDGE 不是必須簽收,消息可能會重複發送。在第二次重新傳送消息的時候,消息
只有在被確認之後,才認爲已經被成功地消費了。消息的成功消費通常包含三個階段:客戶接收消息、客戶處理消息和消息被確認。 在事務性會話中,當一個事務被提交的時候,確認自動發生。在非事務性會話中,消息何時被確認取決於創建會話時的應答模式(acknowledgement mode)。該參數有以下三個可選值:

3.4 Java 實現ActiveMQ Pub/Sub模式:

消息發送者:

package com.zzg.activemq.pubsub;

import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
/**
 * 消息發送者
 * @author Administrator
 *
 */
public class TOPSend {
	private static String BROKERURL = "tcp://127.0.0.1:61616";
	private static String TOPIC = "my-topic";

	public static void main(String[] args) throws JMSException {
		start();
	}

	static public void start() throws JMSException {
		System.out.println("生產者已經啓動....");
		// 創建ActiveMQConnectionFactory 會話工廠
		ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
				ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, BROKERURL);
		Connection connection = activeMQConnectionFactory.createConnection();
		// 啓動JMS 連接
		connection.start();
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		MessageProducer producer = session.createProducer(null);
		producer.setDeliveryMode(DeliveryMode.PERSISTENT);
		send(producer, session);
		System.out.println("發送成功!");
		connection.close();
	}

	static public void send(MessageProducer producer, Session session) throws JMSException {
		for (int i = 1; i <= 5; i++) {
			System.out.println("我是消息" + i);
			TextMessage textMessage = session.createTextMessage("我是消息" + i);
			Destination destination = session.createTopic(TOPIC);
			producer.send(destination, textMessage);
		}
	}
}

消息消費者:

package com.zzg.activemq.pubsub;

import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * 消息接受者
 * @author Administrator
 *
 */
public class TopReceiver {

	private static String BROKERURL = "tcp://127.0.0.1:61616";
	private static String TOPIC = "my-topic";

	public static void main(String[] args) throws JMSException {
		start();
	}

	static public void start() throws JMSException {
		System.out.println("消費點啓動...");
		// 創建ActiveMQConnectionFactory 會話工廠
		ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
				ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, BROKERURL);
		Connection connection = activeMQConnectionFactory.createConnection();
		// 啓動JMS 連接
		connection.start();
		// 不開消息啓事物,消息主要發送消費者,則表示消息已經簽收
		Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
		// 創建一個隊列
		Topic topic = session.createTopic(TOPIC);
		MessageConsumer consumer = session.createConsumer(topic);
		// consumer.setMessageListener(new MsgListener());
		while (true) {
			TextMessage textMessage = (TextMessage) consumer.receive();
			if (textMessage != null) {
				System.out.println("接受到消息:" + textMessage.getText());
				// textMessage.acknowledge();// 手動簽收
				// session.commit();
			} else {
				break;
			}
		}
		connection.close();
	}

}

第四 SpringBoot集成消息中間件ActiveMQ 

參考文章地址:https://www.cnblogs.com/yufeng218/p/11509486.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章