消息隊列已然成爲當下非常火熱的中間件,而rocketmq作爲阿里開源的中間件產品,歷經數次超大併發的考驗,已然成爲中間件產品的首選。而有時候我們在使用消息隊列的時候,往往需要能夠保證消息的順序消費,而rocketmq是可以支持消息的順序消費的。rocketmq在發送消息的時候,是將消息發送到不同的隊列(queue,也有人稱之爲分區)中,然後消費端從多個隊列中讀取消息進行消費,很明顯,在這種全局模式下,是無法實現順序消費的。爲了實現順序消費,我們需要把有順序的消息按照他的順序,將他們發送到同一個queue中,這樣消費端在消費的時候,就保證了其順序。但是順序消費的性能肯定也相對差一些,因爲只能使用一個隊列。
好了,接下來我們使用springboot來看一下順序消費是如何實現的。 官網上給出了一個順序消費的案例,但是都是通過main方法的形式演示的(http://rocketmq.apache.org/docs/order-example/)。
一. 添加依賴:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
二. 配置rocketmq地址(需要先自己搭建好rocketmq服務)
在application.yml中配置:
rocketmq:
name-server: 192.168.1.11:9876;192.168.1.12:9876;192.168.1.13:9876
producer:
group: my-group1
sendMessageTimeout: 300000
這裏我使用的rocketmq的集羣,如果是單機版,name-server只寫一個地址即可
三. 一個簡單的生產消費案例:
我們使用controller 來下一個生產者,這樣當我通過瀏覽器發起請求是,就調用生產者來生產一條消息,同時寫一個消費者,來監聽對應的消息,實現消費
生產者代碼:
@RestController
@RequestMapping("/mq")
@Slf4j
public class ProducerController {
@Resource
private RocketMQTemplate rocketMQTemplate;
@RequestMapping("/sync/send1")
public String syncSendString(){
//發送一個同步 消息,會返回值 ---發送到 stringTopic主題
SendResult sendResult = rocketMQTemplate.syncSend("topicTest", "Hello, World!");
System.out.printf("syncSend1 to topic %s sendResult=%s %n", stringTopic, sendResult);
//consumer result:------- StringConsumerNewNS received: Hello, World!
return sendResult.toString();
}
}
上面的案例,就是我向"topicTest" 的主題中發送一個 Hello,World 的字符串
消費者代碼:
/**
* RocketMQMessageListener
*/
@Service
@RocketMQMessageListener(nameServer = "${rocketmq.nameserver}", topic = "topicTest", consumerGroup = "string_consumer")
public class StringConsumerNewNS implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
System.out.printf("------- StringConsumerNewNS received: %s \n", message);
}
}
這個消費者需要綁定nameserver, 然後監聽 topicTest 這個主題。要注意,要把這個消費者放到能夠被spring容器掃描到的地方。
接下來啓動服務: 在瀏覽器訪問: localhost:{your port}/mq/sync/send1 此時就會生產出一條消息,觀察控制檯,就會看到Hello, World打印出來,代表消息消費成功
四. 實現順序消費
生產者: 此時要生產多條消息,方便觀察順序,我們依然寫一個controller
/**************驗證rocketmq順序消費***************/
@RequestMapping("/send/ordered")
public String sendOrderedMsg(){
/**
* hashKey: 爲了保證報到同一個隊列中,將消息發送到orderTopic主題上
*/
rocketMQTemplate.syncSendOrderly("orderTopic","no1","order");
rocketMQTemplate.syncSendOrderly("orderTopic","no2","order");
rocketMQTemplate.syncSendOrderly("orderTopic","no3","order");
rocketMQTemplate.syncSendOrderly("orderTopic","no4","order");
return "success";
}
這裏要注意,我是向 orderTopic主題發送4條消息,內容分別是 no1 no2 no3 no4. 第三個參數是order ,他的作用是會根據他的hash值計算髮送到哪一個隊列,我用的是同一個值order,那麼他們的hash一樣就可以保證發送到同一個隊列裏
消費者。要注意,消費者在消費的時候,默認是異步多線程消費的,所以無法保證順序,我們要指定同步消費纔行;先看代碼:
/**
* 監聽順序消息,保證順序繳費
*/
@Component
@Slf4j
@RocketMQMessageListener(topic = "orderTopic", consumerGroup = "ordered-consumer",consumeMode = ConsumeMode.ORDERLY)
public class OrderedMsqConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("consumer 順序消費,收到消息{}",message);
}
}
這裏邊指定了 consumeMode = ConsumeMode.ORDERLY, 默認值是 consumeMode = ConsumeMode.CONCURRENT
修改完畢後,啓動項目;
瀏覽器訪問:http://localhost:8888/mq/send/ordered
觀察控制檯日誌,順序打印: no1 no2 no3 no4
好了實現了順序消費;相關源碼已上傳至github: https://github.com/lsqingfeng/action/ (springboot分支)歡迎大家關注交流