同步傳送
生產者
// 建立一個生產者 (生產者組為 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();
順序訊息
- 因為消費者預設併發模式,也就是多執行緒,多執行緒是需要搶奪 cpu 排程權的,多執行緒天生不能保證順序
- 就算改為單執行緒也不能保證順序,假設 Topic 2 個佇列,一個是空的一個有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();