RocketMQ簡介
筆者使用的是Apache RocketMQ,官網是http://rocketmq.apache.org/。RocketMQ是Alibaba開源的一個分佈式消息隊列,可以通過http://rocketmq.apache.org/dowloading/releases/下載當前最新的版本。下載後解壓縮,然後通過bin/mqnamesrv
啓動一個Name Server,它默認監聽在9876端口。然後需要通過bin/mqbroker -n localhost:9876
啓動一個Broker,並把它註冊到剛剛啓動的那個Name Server上,Broker默認監聽在端口10911上。生產者和消費者都是跟Broker打交道,但是它們不會直接指定Broker的地址,而是通過Name Server來間接的獲取Broker的地址。這樣做的好處是可以動態的增加Broker,多個Broker之間可以組成一個集羣。應用中使用RocketMQ時需要添加RocketMQ Client依賴。
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.5.0</version>
</dependency>
然後可以通過DefaultMQProducer進行消息的發送,每一個生產者必須指定一個Group,下面代碼就指定了Group爲group1。相同處理邏輯的生產者必須定義相同的Group。這個Group只對於發送事務消息的生產者有用。然後需要通過setNamesrvAddr()
指定Name Server的地址。在發送消息前必須調用其start()
。發送的消息是通過org.apache.rocketmq.common.message.Message
對象表示的。它必須要指定一個Topic,RocketMQ是通過抽象的Topic來管理一組隊列的,這個Topic必須在Broker中進行創建。可以通過bin/mqadmin updateTopic -b localhost:10911 -t topic1
在本地剛剛啓動的Broker上創建名爲topic1的Topic。它默認擁有8個讀隊列,8個寫隊列。下面的代碼指定了消息都將發送到名爲topic1的Topic。通過其send()
進行消息發送,它是同步發送的,發送完後會返回一個SendResult。其SendStatus爲SEND_OK時表示發送成功。下面的代碼一共發送了10條消息到topic1,消息內容分別是hello0…hello9。
@Test
public void testSend() throws Exception {
//指定Producer的Group爲group1
DefaultMQProducer producer = new DefaultMQProducer("group1");
//指定需要連接的Name Server
producer.setNamesrvAddr(nameServer);
//發送消息前必須調用start(),其內部會進行一些初始化工作。
producer.start();
for (int i = 0; i < 10; i++) {
//指定消息發送的Topic是topic1。
Message message = new Message("topic1", ("hello" + i).getBytes());
//同步發送,發送成功後纔會返回
SendResult sendResult = producer.send(message);
if (sendResult.getSendStatus() == SendStatus.SEND_OK) {
System.out.println("消息發送成功:" + sendResult);
} else {
System.out.println("消息發送失敗:" + sendResult);
}
}
//使用完畢後需要把Producer關閉,以釋放相應的資源
producer.shutdown();
}
消息的消費者可以通過DefaultMQPushConsumer進行消費。DefaultMQPushConsumer是進行推模式消費的,它也需要指定一個Group。默認情況下相同Group的消費者將對同一個隊列中的消息進行集羣消費,即同一條消息只會被一個Consumer實例進行消費。DefaultMQPushConsumer也需要通過setNamesrvAddr()
指定需要連接的Name Server。通過subscribe()
指定需要消費的Topic和對應的Tag。下面指定了需要消費的Topic是topic1,通過*
指定將消費所有的Tag。Tag是用來對消息進行分類標記的,需要在發送消息的時候指定。通過registerMessageListener()
註冊消息監聽器,當收到消息後會回調它。下面代碼註冊的是一個MessageListenerConcurrently類型的監聽器。消息正常消費後需要返回CONSUME_SUCCESS,如果消費失敗可以返回RECONSUME_LATER,這樣可以先跳過這一條消息的消費,Broker會過一段時間再投遞這一條消息。Consumer也是需要通過start()
進行啓動。這樣消費者就可以開始進行消息消費了,默認只有它啓動之後發送的消息才能收到。
@Test
public void testConsumer() throws Exception {
//創建Consumer並指定消費者組。
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group1");
//指定需要連接的Name Server
consumer.setNamesrvAddr(nameServer);
//訂閱topic1上的所有Tag。
consumer.subscribe("topic1", "*");
//註冊一個消息監聽器
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
System.out.println(Thread.currentThread().getName() + "收到了消息,數量是:" + msgs.size());
AtomicInteger counter = new AtomicInteger();
msgs.forEach(msg -> System.out.println(counter.incrementAndGet() + ".消息內容是:" + new String(msg.getBody())));
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//啓動消費者
consumer.start();
//爲了確保Junit線程不立即死掉。
TimeUnit.SECONDS.sleep(120);
}
(注:本文是基於Apache RocketMQ4.5.0所寫)