1.消息消費
public class Consumer {
public static void main(String[] args) throws InterruptedException, MQClientException {
// 實例化消費者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name");
// 設置NameServer的地址
consumer.setNamesrvAddr("localhost:9876");
// 訂閱一個或者多個Topic,以及Tag來過濾需要消費的消息
consumer.subscribe("TopicTest", "*");
// 註冊回調實現類來處理從broker拉取回來的消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
// 標記該消息已經被成功消費
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 啓動消費者實例
consumer.start();
System.out.printf("Consumer Started.%n");
}
}
2.順序消費
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
package org.apache.rocketmq.example.order2;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;
/**
* 順序消息消費,帶事務方式(應用可控制Offset什麼時候提交)
*/
public class ConsumerInOrder {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_3");
consumer.setNamesrvAddr("127.0.0.1:9876");
/**
* 設置Consumer第一次啓動是從隊列頭部開始消費還是隊列尾部開始消費<br>
* 如果非第一次啓動,那麼按照上次消費的位置繼續消費
*/
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("TopicTest", "TagA || TagC || TagD");
consumer.registerMessageListener(new MessageListenerOrderly() {
Random random = new Random();
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
context.setAutoCommit(true);
for (MessageExt msg : msgs) {
// 可以看到每個queue有唯一的consume線程來消費, 訂單對每個queue(分區)有序
System.out.println("consumeThread=" + Thread.currentThread().getName() + "queueId=" + msg.getQueueId() + ", content:" + new String(msg.getBody()));
}
try {
//模擬業務邏輯處理中...
TimeUnit.SECONDS.sleep(random.nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.println("Consumer Started.");
}
}
3.PushConsumer,PullConsumer消費模式分析
Push:實時性高,但是增加服務器負載,消費能力不同,如果push過快,消費端會出現很多問題;
Pull:消費者從Server端拉取資源,主動權在消費端,可控性好,但是間隔時間不好控制,間隔時間太短,則空請求,資源浪費,間隔時間太長,則消息不能及時處理;
長輪詢:Client請求Server服務端(Broker),Broker會保持一段時間的連接,默認是15s,如果這段時間沒有消息達到,則離開返回給Customer,沒有消息的話,超過15s,則返回空,再進行重新請求,缺點:服務端需要保持Customer的請求,會佔用資源,需要客戶端連接數可控,否則會一段連接。
PushConsumer:本質是長輪詢;
1.系統收到消息後自動處理消息和offset,如果有新的Consumer加入會自動做負載均衡,
2.在broker端可以通過longPolling=true來開啓長輪詢,
3.消費端代碼:DefaultMQPushConsumerImpl->pullMessage->PullCallback
4.服務端代碼:broker.longpolling
5.雖然是push,但代碼裏面大量使用了pull,是因爲使用了長輪詢方式達到push效果,既有pull有的,又有push的實現性。
6.關閉優雅:只要是釋放資源和保存offset,調用shutdown()即可,參考@PostConstruct,@PreDestory。
PullConsumer:需要自己維護offset;
1.獲取MessageQueue遍歷;
2.客戶維護offset,需要本地用戶存儲offset,存儲內存,磁盤數據庫等;
3.處理不同狀態的消息FOUND,NO_NEW_MSG,OFFSET_ILLRGL,NO_MATCHED_MSG,4種狀態;
4.靈活性強,但編碼複雜度高;
5.關閉優雅,注意是釋放資源和保存offset,需要程序自己保存offset,特別是異常處理的時候;