ActiveMQ常見的兩種應用模式以及API說明

1 PTP處理模式(Queue)

1.1 ptp基本理解

  • 消息生產者生產消息發送到 queue 中, 然後消息消費者從 queue 中取出並且消費消息。
  • 消息被消費以後, queue 中不再有存儲, 所以消息消費者不可能消費到已經被消費的消息。
  • Queue 支持存在多個消費者, 但是對一個消息而言, 只會有一個消費者可以消費、 其它的則不能消費此消息了。
  • 當消費者不存在時, 消息會一直保存, 直到有消費消費
    在這裏插入圖片描述

1.2 pom依賴

<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-core -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-core</artifactId>
			<version>5.7.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/javax.jms/jms -->
		<dependency>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
			<version>1.1</version>
		</dependency>

1.3 ptp的消費者

public class ConsumerPTP {
	
	public String consumer() {
		//連接工廠
		ConnectionFactory factory = null;
		//連接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//會話
		Session session = null;
		//消息發送者
		MessageConsumer consumer = null;
		//消息對象
		Message message = null;
		String resultCode = null;
		try {
			//創建連接工廠
			//創建工廠三個參數:用戶名,密碼,連接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通過工廠創建連接對象
			//創建連接的方法有重載,其中有createConnection(String username,String password)
			//可以在創建連接時,只傳遞地址 ,不傳遞用戶信息
			connection = factory.createConnection();
			//建議啓動連接,消息的發送者不是必須啓動連接,但是消息的消費者必須啓動連接
			connection.start();
			//兩個參數:boolean transacted 是否支持事物, int acknowledgeMode如何確認消息
			
			session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			//創建目的地。參數名稱是目的地是唯一標記
			destination=session.createQueue("first-activemq");
			//通過會話,創建消息的發送者producer
			//創建的消息發送者,發送的消息一定到指定的目的地中
			consumer = session.createConsumer(destination);
			//獲取隊列中的消息。receive方法是一個主動獲取消息的方法。執行一次拉取一個消息
			message = consumer.receive();
			resultCode = ((TextMessage)message).getText();
			System.out.println("========消息已經發送========");
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(consumer != null) {//回收消息發送者
				try {
					consumer.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null) {//回收會話對象
				try {
					session.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null) {//回收連接對象
				try {
					connection.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			
		}
		return resultCode;
	}
	public static void main(String[] args) {
		ConsumerPTP ptp = new ConsumerPTP();
		System.out.println("========="+ptp.consumer()+"=========");
	}
}

1.4 ptp的提供者

public class ProviderPTP {
	/**
	 * 把消息發送到activemq中,具體消息內容爲參數信息
	 * 開發jms相關代碼過程中,都在javax.jms包下的類型
	 * @param datas - 消息內容
	 */
	public void provider(String datas) {
		//連接工廠
		ConnectionFactory factory = null;
		//連接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//會話
		Session session = null;
		//消息發送者
		MessageProducer producer = null;
		//消息對象
		Message message = null;
		try {
			//創建連接工廠
			//創建工廠三個參數:用戶名,密碼,連接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通過工廠創建連接對象
			//創建連接的方法有重載,其中有createConnection(String username,String password)
			//可以在創建連接時,只傳遞地址 ,不傳遞用戶信息
			connection = factory.createConnection();
			//建議啓動連接,消息的發送者不是必須啓動連接,但是消息的消費者必須啓動連接
			connection.start();
			//連個參數:boolean transacted 是否支持事物, int acknowledgeMode如何確認消息
			/**
			 * transacted:是否支持事物,
			 * true:支持事物,那麼第二個參數默認無效建議是Session.SESSION_TRANSACTED
			 * false:不支持事物,常用參數。那麼第二個參數必須傳,且有效
			 * acknowledgeMode:如何確認消息的處理
			 * AUTO_ACKNOWLEDGE--自動確認消息,消息的消費者處理消息後,自動確認,商業開發不推薦
			 * CLIENT_ACKNOWLEDGE--客戶端手動確認,消息的消費者處理後,必須手工確認
			 * DUPS_OK_ACKNOWLEDGE--有副本的客戶端手動確認,一個消息可以多次處理,可以降低session消耗,可以容忍重複消息時使用(不推薦)
			 */
			session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			//創建目的地。參數名稱是目的地是唯一標記
			destination=session.createQueue("first-activemq");
			//通過會話,創建消息的發送者producer
			//創建的消息發送者,發送的消息一定到指定的目的地中
			producer = session.createProducer(destination);
			//創建文本消息對象,作爲具體數據內容的載體
			message= session.createTextMessage(datas);
			//使用producer,發送消息到activemq中的目的地,若失敗則拋出異常
			producer.send(message);
			
			System.out.println("========消息已經發送========");
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(producer != null) {//回收消息發送者
				try {
					producer.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null) {//回收會話對象
				try {
					session.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null) {//回收連接對象
				try {
					connection.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			
		}
	}
	public static void main(String[] args) {
		ProviderPTP ptp = new ProviderPTP();
		ptp.provider("測試activemq");
	}
}

2 Publish/Subscribe 處理模式(Topic)

2.1 topic基本理解

  • 消息生產者(發佈) 將消息發佈到 topic 中, 同時有多個消息消費者(訂閱) 消費該消息。
  • 和點對點方式不同, 發佈到 topic 的消息會被所有訂閱者消費。
  • 當生產者發佈消息, 不管是否有消費者。 都不會保存消息
  • 一定要先有消息的消費者, 後有消息的生產者。
    在這裏插入圖片描述

2.2 pom依賴

<!-- https://mvnrepository.com/artifact/org.apache.activemq/activemq-core -->
		<dependency>
			<groupId>org.apache.activemq</groupId>
			<artifactId>activemq-core</artifactId>
			<version>5.7.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/javax.jms/jms -->
		<dependency>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
			<version>1.1</version>
		</dependency>

2.3 topic的消費者

public class ConsumerTopic {
	public String consumer() {
		//連接工廠
		ConnectionFactory factory = null;
		//連接
		Connection connection = null;
		//目的地
		Destination destination = null;
		//會話
		Session session = null;
		//消息發送者
		MessageConsumer consumer = null;
		//消息對象
		Message message = null;
		String resultCode = null;
		try {
			//創建連接工廠
			//創建工廠三個參數:用戶名,密碼,連接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通過工廠創建連接對象
			//創建連接的方法有重載,其中有createConnection(String username,String password)
			//可以在創建連接時,只傳遞地址 ,不傳遞用戶信息
			connection = factory.createConnection();
			//建議啓動連接,消息的發送者不是必須啓動連接,但是消息的消費者必須啓動連接
			connection.start();
			//兩個參數:boolean transacted 是否支持事物, int acknowledgeMode如何確認消息
			
			session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
			//創建目的地。參數名稱是目的地是唯一標記
			destination=session.createTopic("test-topicmq");
			//通過會話,創建消息的發送者producer
			//創建的消息發送者,發送的消息一定到指定的目的地中
			consumer = session.createConsumer(destination);
			//獲取隊列中的消息。receive方法是一個主動獲取消息的方法。執行一次拉取一個消息
			message = consumer.receive();
			resultCode = ((TextMessage)message).getText();
			System.out.println("========消息已經發送========");
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(consumer != null) {//回收消息發送者
				try {
					consumer.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null) {//回收會話對象
				try {
					session.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null) {//回收連接對象
				try {
					connection.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			
		}
		return resultCode;
	}	
	public static void main(String[] args) {
		ConsumerTopic ptp = new ConsumerTopic();
		System.out.println("========="+ptp.consumer()+"=========");
	}
}

2.4 topic的提供者

public class ProviderTopic {

	public static void main(String[] args) {
		ProviderTopic ptp = new ProviderTopic();
		ptp.provider("測試topic activemq");
	}

	public void provider(String datas) {
		// 連接工廠
		ConnectionFactory factory = null;
		// 連接
		Connection connection = null;
		// 目的地
		Destination destination = null;
		// 會話
		Session session = null;
		// 消息發送者
		MessageProducer producer = null;
		// 消息對象
		Message message = null;
		try {
			//創建連接工廠
			//創建工廠三個參數:用戶名,密碼,連接地址
			factory = new ActiveMQConnectionFactory("admin","admin","tcp://192.168.126.133:61616");
			//通過工廠創建連接對象
			//創建連接的方法有重載,其中有createConnection(String username,String password)
			//可以在創建連接時,只傳遞地址 ,不傳遞用戶信息
			connection = factory.createConnection();
			//建議啓動連接,消息的發送者不是必須啓動連接,但是消息的消費者必須啓動連接
			connection.start();
			//連個參數:boolean transacted 是否支持事物, int acknowledgeMode如何確認消息
			session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
			//創建目的地。參數名稱是目的地是唯一標記
			//destination=session.createQueue("first-activemq");
			session.createTopic("test-topicmq");
			//通過會話,創建消息的發送者producer
			//創建的消息發送者,發送的消息一定到指定的目的地中
			producer = session.createProducer(destination);
			//創建文本消息對象,作爲具體數據內容的載體
			message= session.createTextMessage(datas);
			//使用producer,發送消息到activemq中的目的地,若失敗則拋出異常
			producer.send(message);
			
			System.out.println("========消息已經發送========");
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(producer != null) {//回收消息發送者
				try {
					producer.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(session != null) {//回收會話對象
				try {
					session.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
			if(connection != null) {//回收連接對象
				try {
					connection.close();
				}catch(JMSException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

3 PTP 和 PUB/SUB 簡單對比

Topic Queue
概要 Publish Subscribe messaging
發佈訂閱消息
Point-to-Point
點對點
有無狀態 topic 數據默認不落地, 是無狀態的 Queue 數據默認會在mq服務器上以文件形式保存,
比如Active MQ 一 般 保 存 在$AMQ_HOME\data\kahadb 下面。
也可以配置成 DB 存儲
完整性保障 並不保證publisher發佈的每條數據, Subscriber 都能接受到 Queue 保證每條數據能被 receiver 接收。 消息不超時
消息是否會丟失 一般來說 publisher 發佈消息到某一個topic時,
只有正在監聽該 topic地址的sub 能夠接收到消息;
如果沒有 sub 在監聽, 該topic 就丟失了
Sender發 送 消 息 到 目 標Queuereceiver 可以異步接收這個 Queue 上的消息。
Queue 上的消息如果暫時沒有 receiver 來取, 也不會丟失。 前提是消息不超時
消息發佈接收策略 一對多的消息發佈接收策略, 監聽同一個 topic 地址的多個 sub 都能收到 publisher發送的消息。
Sub接收完通知 mq 服務器
一對一的消息發佈接收策略, 一個 sender 發送的消息, 只能有一個receiver 接收。
receiver接收完後, 通知 mq 服務器已接收, mq 服務器對 queue 裏的消息採取刪除或其他操作

4 API

4.1 Producer API

4.1.1發送消息

MessageProducer

  • send(Message message);發送消息到默認目的地, 就是創建 Producer 時指定的目的地。
  • send(Destination destination, Message message); 發送消息到指定目的地, Producer不建議綁定目的地。也就是創建 Producer的時候,不綁定目的地。這樣寫法:session.createProducer(null)
  • send(Message message, int deliveryMode, int priority, long timeToLive);發送消息到默認目的地, 且設置相關參數。
    deliveryMode:持久化方式(DeliveryMode.PERSISTENT|DeliveryMode.NON_PERSISTENT)。
    priority:優先級。 timeToLive:消息有效期(單位毫秒)。
  • send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive)發送消息到指定目的地, 且設置相關參數。

4.1.2 消息有效期

消息過期後, 默認會將失效消息保存到死信隊列(ActiveMQ.DLQ)
不持久化的消息, 在超時後直接丟棄, 不會保存到死信隊列中。
死信隊列名稱可配置, 死信隊列中的消息不能恢復
死信隊列是在activemq.xml 中配置的。

4.1.3 消息優先級

可以在發送消息時, 指定消息的權重,broker 可以建議權重較高的消息將會優先發送給 Consumer。 在某些場景下, 我們通常希望權重較高的消息優先傳送; 不過因爲各種原因, priority 並不能決定消息傳送的嚴格順序(order)。
JMS 標準中約定 priority 可以爲0~9 的整數數值, 值越大表示權重越高,默認值爲 4
activeMQ 中各個存儲器對 priority 的支持並非完全一樣。 比如 JDBC 存儲器可以支持0~9,因爲JDBC 存儲器可以基於 priority 對消息進行排序和索引化;但是對於 kahadb/levelDB等這種基於日誌文件的存儲器而言, priority 支持相對較弱, 只能識別三種優先級(LOW: <4,NORMAL: =4,HIGH: > 4)

4.1.3.1 開啓

broker端, 默認是不存儲 priority 信息的, 我們需要手動開啓, 修改 activemq.xml配置文件, 在 broker標籤的子標籤 policyEntries中增加下述配置:

<policyEntry queue=">" prioritizedMessages="true"/>

不過對於“非持久化”類型的消息(如果沒有被 swap 到臨時文件), 它們被保存在內存中,它們不存在從文件Paged in 到內存的過程,因爲可以保證優先級較高的消息,總是在prefetch的時候被優先獲取, 這也是“非持久化”消息可以擔保消息發送順序的優點。
Broker在收到Producer的消息之後, 將會把消息cache到內存, 如果消息需要持久化,那麼同時也會把消息寫入文件; 如果通道中 Consumer的消費速度足夠快(即積壓的消息很少, 尚未超過內存限制, 通過上文能夠知道, 每個通道都可以有一定的內存用來 cache消息), 那麼消息幾乎不需要從存儲文件中Paged In, 直接就能從內存的cache中獲取即可,這種情況下, priority 可以擔保“全局順序”; 不過, 如果消費者滯後太多,cache已滿, 就會觸發新接收的消息直接保存在磁盤中, 那麼此時,priority 就沒有那麼有效了。
Queue 中, prefetch 的消息列表默認將會採用輪詢的方式(roundRobin, 注意並不是roundRobinDispatch)[備註:因爲 Queue不支持任何 DispatchPolicy],依次添加到每個 consumerpending buffer 中, 比如有 m1-m2-m3-m4四條消息, 有 C1-C2兩個消費者, 那麼 :m1->C1,m2->C2,m3->C1,m4->C2。 這種輪序方式, 會對基於權重的消息發送有些額外的影響, 假如四條消息的權重都不同, 但是(m1,m3)->C1, 事實上m2的權重>m3,對於 C1 而言,它似乎丟失了“順序性”。

4.1.3.2 強順序

<policyEntry queue=">" strictOrderDispatch="true"/>

strictOrderDispatch“嚴格順序轉發”, 這是區別於“輪詢”的一種消息轉發手段; 不過不要誤解它爲“全局嚴格順序”, 它只不過是將prefetch 的消息依次填滿每個consumerpending buffer 。 比 如 上 述 例 子 中 , 如 果C1-C2兩 個 消 費 者 的buffer尺 寸 爲 3 , 那 麼(m1,m2,m3)->C1,(m4)->C2;當 C1填充完畢之後, 纔會填充 C2。由此這種策略可以保證 buffer中所有的消息都是“權重臨近的”、 有序的。 (需要注意: strictOrderDispatch 並非是解決priority消息順序的問題而生, 只是在使用 priority時需要關注它)。

4.1.3.3 嚴格順序

<policyEntry queue=">" prioritizedMessages="true" useCache="false"
expireMessagesPeriod="0" queuePrefetch="1"/>

useCache=false來關閉內存, 強制將所有的消息都立即寫入文件(索引化, 但是會降低消息的轉發效率);queuePrefetch=1來約束每個consumer任何時刻只有一個消息正在處理, 那些消息消費之後, 將會從文件中重新獲取, 這大大增加了消息文件操作的次數, 不過每次讀取肯定都是 priority最高的消息

4.2 Consumer API

4.2.1 消息的確認

Consumer拉取消息後, 如果沒有做確認 acknowledge, 此消息不會從 MQ中刪除。
如果消息拉去到consumer 後, 未確認, 那麼消息被鎖定。 如果 consumer關閉的時候仍舊沒有確認消息, 則釋放消息鎖定信息。 消息將發送給其他的consumer處理。
消息一旦處理, 應該必須確認。 類似數據庫中的事務管理機制。
例:consumer確認方法(Session.CLIENT_ACKNOWLEDGE客戶端手動確認時才需要如下,若是AUTO_ACKNOWLEDGE這不需要了)

message.acknowledge()

4.2.2 消息的過濾

對消息消費者處理的消息數據進行過濾。 這種處理可以明確消費者的角色, 細分消費者的功能。
設置過濾:
Session.createConsumer(Destination destination, String messageSelector);
過濾信息爲字符串, 語法類似 SQL92 中的 where子句條件信息。 可以使用諸如AND、OR、 IN、 NOT IN等關鍵字。 詳細內容可以查看javax.jms.Message 的幫助文檔。
注意: 消息的生產者在發送消息的的時候, 必須設置可過濾的屬性信息, 所有的屬性信息設置方法格式爲:setXxxxProperty(String name, T value)。 其中方法名中的Xxxx 是類型,如 setObjectProperty/setStringProperty 等。

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