RocketMq學習記錄

RocketMQ的部署模型

 

 

 在RocketMq中有四個部分組成,分別是Producer,Consumer,Broker,以及NameServer。

生產者 Producer

發佈消息的角色。Producer通過 MQ 的負載均衡模塊選擇相應的 Broker 集羣隊列進行消息投遞

消費者 Consumer

消費消息的角色。消費者支持pull和push兩種模式對消息進行消費。其中push又支持兩種push方式:一種是比較原始Pull Consumer,它不提供相關的訂閱方法;另一種是Lite Pull Consumer,它提供了Subscribe和Assign兩種方式

名字服務器 NameServer

NameServer是一個簡單的 Topic 路由註冊中心,支持 Topic、Broker 的動態註冊與發現。平時主要用於維護心跳檢測,和提供Topic-Broker之間的關係數據。

代理服務器 Broker

Broker主要負責消息的存儲、投遞和查詢功能NameServer將保存關於 Broker 集羣的整個路由信息和用於客戶端查詢的隊列信息。Producer和Consumer通過NameServer就可以知道整個Broker集羣的路由信息,從而進行消息的投遞和消費

RocketMQ工作流程

1. 啓動NameServer

啓動NameServer。NameServer啓動後監聽端口,等待Broker、Producer、Consumer連接,相當於一個路由控制中心。

2. 啓動 Broker

啓動 Broker。與所有 NameServer 保持長連接,定時發送心跳包。心跳包中包含當前 Broker 信息以及存儲所有 Topic 信息。註冊成功後,NameServer 集羣中就有 Topic跟Broker 的映射關係。

3. 創建 Topic

創建 Topic 時需要指定該 Topic 要存儲在哪些 Broker 上,也可以在發送消息時自動創建Topic。

4. 生產者發送消息

生產者發送消息。啓動時先跟 NameServer 集羣中的其中一臺建立長連接,並從 NameServer 中獲取當前發送的 Topic存在於哪些 Broker 上,輪詢從隊列列表中選擇一個隊列,然後與隊列所在的 Broker建立長連接從而向 Broker發消息。

5. 消費者接受消息

消費者接受消息。跟其中一臺NameServer建立長連接,獲取當前訂閱Topic存在哪些Broker上,然後直接跟Broker建立連接通道,然後開始消費消息。

基本概念

消息

一個消息必須有一個主題(Topic),消息也可以擁有一個可選的標籤(Tag)和額處的鍵值對,它們可以用於設置一個業務 Key 並在 Broker 上查找此消息以便在開發期間查找問題。

TOPIC

消息主題,通過 Topic 對不同的業務消息進行分類。

Tag

消息標籤,用來進一步區分某個 Topic 下的消息分類,消息從生產者發出即帶上的屬性。

Keys

每個消息可以在業務層面的設置唯一標識碼 keys 字段,方便將來定位消息丟失問題,應用可以通過 topic、key 來查詢這條消息內容

隊列

一個Topic可能會有多個隊列,並且可能分配在不同的Borker上。一般來說一條消息,如果沒有重複發送,則只會存在在 Topic 的其中一個隊列中,消息在隊列中按照FIFO先進先出的原則存儲

消息發送

普通消息發送

RocketMQ可用於以三種方式發送消息:同步、異步和單向傳輸。前兩種消息類型是可靠的。

同步發送

消息發送方發送一條消息後,會在收到服務端同步響應之後發送下一條消息

DefaultMQProducer producer = new DefaultMQProducer("rocketmq_producer_group_1");
producer.setNamesrvAddr("localhost:9876");
producer.start();
try {
	Message msg = new Message("TopicTest" ,"TagA" ,("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET));
        //使用send進行同步發送
	SendResult sendResult = producer.send(msg);
} catch (Exception e) {
	e.printStackTrace();
}
producer.shutdown();

異步發送

異步方式是指發送出消息之後,不等服務器返回響應,接着發送下一條消息。發送方通過回調接口接受服務端的響應

DefaultMQProducer producer = new DefaultMQProducer("rocketmq_producer_group_1");
producer.setNamesrvAddr("localhost:9876");
producer.start();
try {
	Message msg = new Message("TopicTest" ,"TagA" ,("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET));
	   //異步發送,還是調用send方法,但是需要傳入sendCallback回調
	   producer.send(msg, new SendCallback() {
		@Override
		public void onSuccess(SendResult sendResult) {
		}
		@Override
		public void onException(Throwable e) {
		}
	  });
} catch (Exception e) {
	e.printStackTrace();
}
producer.shutdown();

單向模式

只管發送,不等待服務器是否響應且沒有回調函數,一句話,只管發。

DefaultMQProducer producer = new DefaultMQProducer("rocketmq_producer_group_1");
producer.setNamesrvAddr("localhost:9876");
producer.start();
try {
	Message msg = new Message("TopicTest" ,"TagA" ,("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET));
	//使用sendOneway方法進行單向發送
	producer.sendOneway(msg);
} catch (Exception e) {
	e.printStackTrace();
}
producer.shutdown();

  tips:可以看出同步、異步、單向三種發送方式,區別在於調用的發送方法不同。

順序消息發送

順序發送只指按照FIFO先進先出的方式發送消息和消費消息。RocketMQ消息的順序分爲兩部分,生產順序和消費順序,只有同時滿足才能實現順序消息。

對於同一個Topic,RocketMQ生產者在發佈消息時,可能會將消息發送到不同的隊列中,有可能是這個borker,也有可能是其他的borker。而消費者又是從不同的隊列中消費,所以普通消費無法實現順序消息發送。在RocketMQ中可以對某一個消息進行分區,同一個分區中的消息會被分配到同一個隊列中,並按照順序消費。RokcetMQ只會保證小範圍的順序,即隊列中的順序,無法保證大範圍的順序,即可能先訪問的是這個隊列,然後開始訪問其他的隊列。

DefaultMQProducer producer = new DefaultMQProducer("rocketmq_producer_group_1");
            producer.setNamesrvAddr("localhost:9876");
            producer.start();

            String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
            for (int i = 0; i < 100; i++) {
                int orderId = i % 10;
                Message msg =
                    new Message("order_message", tags[i % tags.length], "KEY" + i,
                        ("order_message_" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
                //使用該方法進行順序發送
                SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
                    @Override
                    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                        Integer id = (Integer) arg;
                        int index = id % mqs.size();
                        return mqs.get(index);
                    }
                }, orderId);
            }
            producer.shutdown();
        } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
            e.printStackTrace();
        }

  調用SendResult send(Message msg, MessageQueueSelector selector, Object arg)方法,MessageQueueSelector 是隊列選擇器,arg 是一個 Java Object 對象,

其中 mqs 是可以發送的隊列,select方法返回一個隊列,意思發送到該隊列中,使用計算方法計算出同一個隊列,則保證了該隊列中的是順序消息。

值得注意的是如果其中一個borker掉線,RocketMQ 提供兩種模式:保證嚴格順序而不是可用性或者保證可用性而不是嚴格順序。

延遲消息發送

延遲消息是指生產者發送消息到RocketMQ後,不希望立馬投遞該消息,而是延遲一定時間後才投遞到消費者進行消費。

Apache RocketMQ 一共支持18個等級的延遲投遞,具體如下:

投遞等級(delay level)
延遲時間
投遞等級(delay level)
延遲時間
1
1s
10
6min
2
5s
11
7min
3
10s
12
8min
4
30s
13
9min
5
1min
14
10min
6
2min
15
20min
7
3min
16
30min
8
4min
17
1h
9
5min
18
2h
DefaultMQProducer producer = new DefaultMQProducer("ScheduledMessageProducer");
producer.setNamesrvAddr("localhost:9876");

producer.start();

Message message = new Message("Topic_Test",
		("Hello RocketMQ").getBytes(RemotingHelper.DEFAULT_CHARSET));
//設置延遲等級
message.setDelayTimeLevel(3);
SendResult send = producer.send(message);
System.out.println(send);
producer.shutdown();

批量消息發送

RocketMQ可以將一些消息凝聚成一批消息後進行發送。

 DefaultMQProducer producer = new DefaultMQProducer("BatchProducerGroupName");
 producer.start();
 String topic = "BatchTest";
 List<Message> messages = new ArrayList<>();
 messages.add(new Message(topic, "Tag", "OrderID001", "Hello world 0".getBytes()));
 messages.add(new Message(topic, "Tag", "OrderID002", "Hello world 1".getBytes()));
 messages.add(new Message(topic, "Tag", "OrderID003", "Hello world 2".getBytes()));

 producer.send(messages);

  將消息打包成List傳入即可,批量消息的大小不能超過1MB,否則會進行拆分,且同一批的消息的Topic必須一致。

事物消息發送

RocketMQ的特色。

 

 

 

事務消息發送步驟如下:

  1. 生產者將半事務消息發送至 RocketMQ Broker
  2. RocketMQ Broker 將消息持久化成功之後,向生產者返回 Ack 確認消息已經發送成功,此時消息暫不能投遞,爲半事務消息。
  3. 生產者開始執行本地事務邏輯。
  4. 生產者根據本地事務執行結果向服務端提交二次確認結果(Commit或是Rollback),服務端收到確認結果後處理邏輯如下:
  • 二次確認結果爲Commit:服務端將半事務消息標記爲可投遞,並投遞給消費者。
  • 二次確認結果爲Rollback:服務端將回滾事務,不會將半事務消息投遞給消費者。
  1. 在斷網或者是生產者應用重啓的特殊情況下,若服務端未收到發送者提交的二次確認結果,或服務端收到的二次確認結果爲Unknown未知狀態,經過固定時間後,服務端將對消息生產者即生產者集羣中任一生產者實例發起消息回查。

  2. :::note 需要注意的是,服務端僅僅會按照參數嘗試指定次數,超過次數後事務會強制回滾

  事務消息回查步驟如下: 7. 生產者收到消息回查後,需要檢查對應消息的本地事務執行的最終結果。 8. 生產者根據檢查得到的本地事務的最終狀態再次提交二次確認,服務端仍按照步驟4對半事務消息進行處理。

消費者概念

RocketMQ 有兩種消費模式,分別是:

  • 集羣消費模式:當使用集羣消費模式時,RocketMQ 認爲任意一條消息只需要被消費組內的任意一個消費者處理即可。
  • 廣播消費模式:當使用廣播消費模式時,RocketMQ 會將每條消息推送給消費組所有的消費者,保證消息至少被每個消費者消費一次。
//集羣模式
consumer.setMessageModel(MessageModel.CLUSTERING);
//廣播模式
consumer.setMessageModel(MessageModel.BROADCASTING);

負載均衡

集羣模式下,同一個消費組(組名一樣)內的消費者會分擔收到的全量消息。默認是的是平均分配,RocketMQ 提供了多種集羣模式下的分配策略。

//設置負載均衡算法 
consumer.setAllocateMessageQueueStrategy(new AllocateMessageQueueAveragely());

消息過濾

消息過濾是指消息生產者向Topic中發送消息時,設置消息屬性對消息進行分類,消費者訂閱Topic時,根據消息屬性設置過濾條件對消息進行過濾,只有符合過濾條件的消息纔會被投遞到消費端進行消費。

消息重試

若Consumer消費某條消息失敗,則RocketMQ會在重試間隔時間後,將消息重新投遞給Consumer消費,若達到最大重試次數後消息還沒有成功被消費,則消息將被投遞至死信隊列,消息重試只針對集羣消費模式生效;廣播消費模式不提供失敗重試特性,即消費失敗後,失敗消息不再重試,繼續消費新的消息

consumer.setMaxReconsumeTimes(10);

死信隊列

當一條消息初次消費失敗,RocketMQ會自動進行消息重試,達到最大重試次數後,若消費依然失敗,則表明消費者在正常情況下無法正確地消費該消息。此時,該消息不會立刻被丟棄,而是將其發送到該消費者對應的特殊隊列中,這類消息稱爲死信消息,存儲死信消息的特殊隊列稱爲死信隊列。

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