Java訊息佇列入門詳解

ApacheRocketMQ發表於2024-10-21

什麼是訊息佇列?

訊息佇列的產生主要是為了解決系統間的非同步解耦與確保最終一致性。在實際應用場景中,往往存在一些主流程操作和輔助流程操作,其中主流程需要快速響應使用者請求,而輔助流程可能涉及複雜的處理邏輯或者依賴於外部服務。透過將這些輔助流程的訊息放入訊息佇列,使得它們可以並行執行,並且不會阻塞主流程的執行,從而提高了系統的整體效能和使用者體驗。同時,訊息佇列支援至少一次的訊息投遞機制,保證了即使在網路不穩定或者其他異常情況下,輔助流程也一定能夠得到執行的機會,進而增強了系統的可靠性和穩定性。

什麼場景要使用訊息佇列

在實際應用場景中,RocketMQ作為一種高效可靠的訊息佇列服務,能夠很好地滿足不同業務需求。以下透過具體場景示例來講解解耦、非同步處理、削峰填谷、可靠性和擴充套件性這幾個關鍵詞。

解耦

假設有一個電商系統,每當使用者下單時需要觸發一系列操作如庫存更新、訂單記錄生成等。如果不使用訊息佇列,則這些邏輯需要序列執行或直接呼叫,這不僅增加了系統的複雜度,也使得各個元件間的依賴關係變得緊密。而採用RocketMQ後,訂單服務可以作為生產者將訂單資訊傳送到特定的主題(Topic)上,後續的庫存服務和訂單記錄服務作為消費者從該主題訂閱並處理相關資訊。這樣即使某個下游服務出現故障,也不會直接影響整個購物流程的正常執行。

非同步處理

考慮一個線上支付平臺,在使用者完成支付動作之後,除了立即返回支付結果給前端外,後臺還需要執行諸如積分更新、優惠券發放等一系列操作。如果所有步驟都同步進行,則會大大增加響應時間。此時利用RocketMQ可以讓支付服務快速確認交易成功並向客戶端反饋,同時非同步地將相關事件釋出出去供其他模組訂閱處理,從而有效提升了使用者體驗和服務效率。

剝峰填谷

對於一些具有明顯訪問高峰期的應用程式來說(比如節假日促銷期間的電商平臺),短時間內湧入大量請求可能會導致伺服器壓力劇增甚至崩潰。藉助於RocketMQ這樣的訊息中介軟體,可以在流量高峰時段將超出處理能力範圍內的請求暫存起來,並以可控速率逐步分發給後端服務進行處理,以此實現流量平滑化,保護核心業務不受衝擊。

可靠性

資料傳輸過程中的安全性和完整性至關重要。例如,在金融交易場景下,每筆交易都需要被準確無誤地記錄下來。RocketMQ支援事務訊息功能,保證了訊息傳送與本地事務的一致性;同時它還提供了訊息持久化儲存機制以及重試策略,確保即便在網路不穩定或者系統異常情況下也能最終完成訊息傳遞,極大增強了系統的健壯性。

擴充套件性

隨著業務規模的增長,往往需要對現有架構進行橫向擴充套件以應對更高的併發請求量。透過引入RocketMQ,我們可以很容易地新增更多的生產者例項來分擔訊息生產任務,或者部署額外的消費者例項以加速訊息消費速度。由於消費者組的存在,新增節點無需改變已有配置即可自動參與到負載均衡中去,從而簡化了擴充套件過程並提高了整體吞吐量。

訊息佇列的主要產品可選項:

線上業務場景適合:
RocketMQ專為高併發、低延遲和高可用性設計,特別適用於金融級的實時交易處理。它支援事務訊息,確保分散式系統中的資料一致性。此外,RocketMQ提供了靈活的訊息型別,包括順序訊息、定時/延時訊息等,非常適合複雜的線上業務需求。

大資料傳輸適合:
Kafka在大規模資料處理場景中表現出色,尤其是日誌收集與流式資料處理。其核心優勢在於單檔案儲存結構,這使得Kafka在處理大量連續寫入的資料時能夠實現極高的效率。因此,對於需要高效處理海量資料的應用來說,Kafka是理想的選擇。

需要JMS標準實現適合:
ActiveMQ作為一款完全相容JMS1.1規範的訊息中介軟體,非常適合那些依賴於Java企業級應用環境的專案。除了提供豐富的訊息協議支援外,ActiveMQ還具備強大的功能集,如持久化訊息儲存、分散式的部署模式等,可以滿足多種複雜的訊息路由需求。

amqp等多細微小場景適合:
RabbitMQ以其對AMQP(高階訊息佇列協議)的支援聞名,同時相容STOMP、MQTT等多種協議,非常適合物聯網裝置間的通訊及微服務架構下的解耦需求。儘管RocketMQ也在增加對這些協議的支援,但目前RabbitMQ仍是這類應用場景下的首選解決方案之一。

詳細的使用MQ收發訊息的例子(以rocketmq為例)

詳細的使用MQ收發訊息的例子(以RocketMQ為例)

1. 加入依賴

首先,在專案的pom.xml檔案中新增對RocketMQ客戶端庫的依賴。這一步是必要的,以便在你的Java應用中使用RocketMQ的功能。

1 <dependency>
2     <groupId>org.apache.rocketmq</groupId>
3 
4     <artifactId>rocketmq-client</artifactId>
5 
6     <version>4.9.1</version>
7 
8 </dependency>

對於Gradle專案,對應的依賴宣告如下:

1 implementation 'org.apache.rocketmq:rocketmq-client:4.9.1'

確保使用的版本與你本地或遠端執行的RocketMQ服務端相容。

2. 訊息傳送

下面是一個簡單的生產者示例,它向指定的主題傳送同步訊息。這裡也展示瞭如何配置和啟動一個DefaultMQProducer例項。

 1 import org.apache.rocketmq.client.producer.DefaultMQProducer;
 2 import org.apache.rocketmq.common.message.Message;
 3 
 4 public class Producer {
 5     public static void main(String[] args) throws Exception {
 6         // 建立生產者例項,並設定生產者組名
 7         DefaultMQProducer producer = new DefaultMQProducer("my-group");
 8         // 設定NameServer地址
 9         producer.setNamesrvAddr("localhost:9876");
10         // 啟動生產者
11         producer.start();
12         
13         for (int i = 0; i < 10; i++) {
14             // 建立一條訊息,指定主題、標籤及訊息內容
15             Message msg = new Message("test-topic-1", "TagA", ("Hello RocketMQ " + i).getBytes());
16             
17             // 傳送訊息到Broker
18             producer.send(msg);
19         }
20         
21         // 關閉生產者
22         producer.shutdown();
23     }
24 }

3. 消費訊息

接下來,展示一個消費者例子,說明如何訂閱特定主題的訊息並處理它們。此例採用了推送模式下的DefaultMQPushConsumer

 1 import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
 2 import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
 3 import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
 4 import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
 5 import org.apache.rocketmq.common.message.MessageExt;
 6 
 7 import java.util.List;
 8 
 9 public class Consumer {
10     public static void main(String[] args) throws Exception {
11         // 建立消費者例項,並設定消費者組名
12         DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("my-consumer_group");
13         // 設定NameServer地址
14         consumer.setNamesrvAddr("localhost:9876");
15         // 訂閱一個或多個Topic,並透過tag過濾所需的訊息
16         consumer.subscribe("test-topic-1", "*");
17         // 註冊訊息監聽器,當有新訊息到達時會觸發onMessage方法
18         consumer.registerMessageListener(new MessageListenerConcurrently() {
19             @Override
20             public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
21                 System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
22                 return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
23             }
24         });
25         // 啟動消費者
26         consumer.start();
27         System.out.printf("Consumer Started.%n");
28     }
29 }

為什麼這樣做可以實現非同步解耦?

透過上述步驟,我們能夠利用RocketMQ實現訊息的非同步通訊,從而達到系統元件之間的解耦效果。生產者將訊息釋出到特定的主題上,而不必關心哪些消費者將會接收這些訊息;同樣地,消費者只需訂閱感興趣的主題即可獲取資訊,無需直接與生產者互動。這種方式不僅簡化了系統的複雜度,還提高了其可擴充套件性和靈活性。例如,在訂單系統中,當使用者下單後,可以立即傳送一個包含訂單詳情的訊息給庫存管理服務,後者可以在空閒時處理該請求而不會阻塞使用者的操作流程。此外,RocketMQ支援多種訊息型別(如順序訊息、延時訊息等),使得開發者可以根據具體需求選擇最適合的訊息模型來進一步最佳化應用程式的設計。

相關文章