rocket 原生 API 使用

CyrusHuang發表於2024-09-05

同步傳送

生產者

// 建立一個生產者  (生產者組為 test-producer-group,生產者組不是那麼重要)
DefaultMQProducer producer = new DefaultMQProducer("test-producer-group");
// 連線 name server
producer.setNamesrvAddr("127.0.0.1:9876");
// 啟動
producer.start();
// 建立10個訊息,訊息將分攤到 4 個佇列上 2-2-3-3
for (int i = 0; i < 10; i++) {
    // 訊息 Topic 為 testTopic
    Message message = new Message("testTopic", "我是一個簡單的訊息".getBytes());
    // 傳送訊息
    SendResult sendResult = producer.send(message);
    System.out.println("訊息傳送狀態:" + sendResult.getSendStatus());
}
// 關閉生產者
producer.shutdown();

消費者

// 建立一個消費者(消費者組為 test-consumer-group)
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test-consumer-group");
// 連線 name server
consumer.setNamesrvAddr("127.0.0.1:9876");
// 訂閱一個主題  * 標識訂閱這個主題中所有的訊息(可以根據 Tag 過濾)
consumer.subscribe("testTopic", "*");
// 設定一個監聽器(MessageListenerConcurrently:併發模式)
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        // 全部內容有很多,包括訊息頭和訊息體,訊息體就是生產者傳送的內容
        System.out.println("訊息全部內容" + msgs.get(0).toString());
        System.out.println("訊息體:" + new String(msgs.get(0).getBody()));
        System.out.println("消費上下文:" + context);
        // 返回值 CONSUME_SUCCESS 成功,訊息會從 mq 出隊
        // 返回值 RECONSUME_LATER 失敗, 訊息會重新回到佇列(預設消費重試16次)
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});
// 啟動
consumer.start();

非同步傳送

生產者

DefaultMQProducer producer = new DefaultMQProducer("async-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
Message message = new Message("asyncTopic", "我是一個非同步訊息".getBytes());
// 回撥方法接收伺服器響應,是非同步的,不阻塞當前
producer.send(message, new SendCallback() {
    @Override
    public void onSuccess(SendResult sendResult) {
        System.out.println("傳送成功");
    }

    @Override
    public void onException(Throwable e) {
        System.err.println("傳送失敗:" + e.getMessage());
    }
});
producer.shutdown();

單向傳送

生產者

DefaultMQProducer producer = new DefaultMQProducer("oneway-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
Message message = new Message("onewayTopic", "我是一個單向訊息".getBytes());
// 單向,發完就不管
producer.sendOneway(message);
producer.shutdown();

批次傳送

生產者

DefaultMQProducer producer = new DefaultMQProducer("batch-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
// 每個訊息指定 Topic
List<Message> msgs = Arrays.asList(
        new Message("batchTopic", "我是一組訊息的A訊息".getBytes()),
        new Message("batchTopic", "我是一組訊息的B訊息".getBytes()),
        new Message("batchTopic", "我是一組訊息的C訊息".getBytes())
);
// 同步傳送一組訊息(這是一次傳送,這批次的訊息會被放到一個佇列上)
SendResult send = producer.send(msgs);
System.out.println(send);
// 關閉例項
producer.shutdown();

消費者

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("batch-consumer-group");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.subscribe("batchTopic", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        // 會列印 3 次,雖然一次傳送多個訊息,但消費的時候還是一個一個來消費的
        System.out.println("收到訊息了" + new Date());
        System.out.println(msgs.size());
        System.out.println(new String(msgs.get(0).getBody()));
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});
consumer.start();

延遲訊息

DefaultMQProducer producer = new DefaultMQProducer("ms-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
Message message = new Message("orderMsTopic", "我是一個延遲訊息".getBytes());
// 給訊息設定一個延遲時間,不同的延遲級別寶石不同的延遲時間
// messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h"
message.setDelayTimeLevel(3);
producer.send(message);
System.out.println("傳送時間" + new Date());
producer.shutdown();

順序訊息

  1. 因為消費者預設併發模式,也就是多執行緒,多執行緒是需要搶奪 cpu 排程權的,多執行緒天生不能保證順序
  2. 就算改為單執行緒也不能保證順序,假設 Topic 2 個佇列,一個是空的一個有3條訊息,消費者會輪詢取訊息
  3. 所以要保證順序就需要滿足兩個條件:1,單執行緒模式消費;2,訊息放到一個佇列或 topic 只有一個佇列

生產者

DefaultMQProducer producer = new DefaultMQProducer("orderly-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
// 傳送一批訊息,根據 msgModel.getOrderSn() 分組,每組訊息放一個佇列
msgModels.forEach(msgModel -> {
    Message message = new Message("orderlyTopic", msgModel.toString().getBytes());
    try {
        // 第一個引數是訊息,第二個引數是選擇的具體佇列,第三個引數是分組的 key
        producer.send(message, new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                // 雜湊值取模是一個週期函式,保證每組訊息輪詢放到不同佇列
                int hashCode = arg.toString().hashCode();
                int i = hashCode % mqs.size();
                return mqs.get(i);
            }
        }, msgModel.getOrderSn()); // msgModel.getOrderSn() 會傳到內部類的方法上

    } catch (Exception e) {
        e.printStackTrace();
    }
});
producer.shutdown();

消費者

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("orderly-consumer-group");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.subscribe("orderlyTopic", "*");
// MessageListenerConcurrently 併發模式(多執行緒消費,消費失敗預設重試16次)
// MessageListenerOrderly 順序模式(單執行緒的,消費失敗預設重試次數為 Integer.Max_Value)
consumer.registerMessageListener(new MessageListenerOrderly() {
    @Override
    public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
        System.out.println("執行緒id:" + Thread.currentThread().getId());
        System.out.println(new String(msgs.get(0).getBody()));
        return ConsumeOrderlyStatus.SUCCESS;
    }
});
consumer.start();

帶 Tag

生產者

DefaultMQProducer producer = new DefaultMQProducer("tag-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
// 指定 Tag 為 vip1
Message message = new Message("tagTopic", "vip1", "vip1訊息".getBytes());
// 指定 Tag 為 vip2
Message message2 = new Message("tagTopic", "vip2", "vip2訊息".getBytes());
// 傳送這兩個訊息
producer.send(message);
producer.send(message2);
producer.shutdown();

消費者1

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("tag-consumer-group-a");
consumer.setNamesrvAddr("127.0.0.1:9876");
// 訂閱 tagTopic,只接收 tag 為 vip1 的訊息
consumer.subscribe("tagTopic", "vip1");
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        System.out.println("消費者1:" + new String(msgs.get(0).getBody()));
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});
consumer.start();

消費者2

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("tag-consumer-group-b");
consumer.setNamesrvAddr("127.0.0.1:9876");
// 訂閱 tagTopic,只接收 tag 為 vip1 或 vip2 的訊息(兩個訊息都會接收)
consumer.subscribe("tagTopic", "vip1 || vip2");
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        System.out.println("消費者2:" + new String(msgs.get(0).getBody()));
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});
consumer.start();

帶 Key

生產者

DefaultMQProducer producer = new DefaultMQProducer("key-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
String key = UUID.randomUUID().toString();
// topic、tag、key
Message message = new Message("keyTopic", "vip1", key, "我是帶key的訊息".getBytes());
producer.send(message);
producer.shutdown();

消費者

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("key-consumer-group");
consumer.setNamesrvAddr("127.0.0.1:9876");
// 不顧慮 tag
consumer.subscribe("keyTopic", "*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        MessageExt messageExt = msgs.get(0);
        System.out.println("訊息主體:" + new String(messageExt.getBody()));
        System.out.println("訊息key:" + messageExt.getKeys());
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});
consumer.start();

重試

生產者

DefaultMQProducer producer = new DefaultMQProducer("retry-producer-group");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
// 傳送失敗重試次數
producer.setRetryTimesWhenSendFailed(2);
producer.setRetryTimesWhenSendAsyncFailed(2);
String key = UUID.randomUUID().toString();
Message message = new Message("retryTopic", "vip1", key, "我是一條重試訊息".getBytes());
producer.send(message);
producer.shutdown();

消費者

/**
 * 重試的時間間隔
 * 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
 * 預設重試16次還是失敗就放入死信佇列,死信佇列的 Topic 為:%DLQ%消費者組名稱,這裡就是 %DLQ%retry-consumer-group
 * 死信佇列的訊息處理為:寫個訂閱這個死信佇列的消費者
 */
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("retry-consumer-group");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.subscribe("retryTopic", "*");
// 設定消費者重試次數,一般業務3-5次消費不成功就要另外處理了,記錄下來,也不用進死信佇列
consumer.setMaxReconsumeTimes(16);
consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        MessageExt messageExt = msgs.get(0);
        // 重試次數
        int times = messageExt.getReconsumeTimes();
        if (times > 3){
            // 3次都消費失敗,記錄下來,返回成功
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        }
        // 業務處理,報錯會視為消費失敗訊息重新回到佇列
		...
    }
});
consumer.start();

相關文章