前面的章節,我們已經把RocketMQ的環境搭建起來了,是一個兩主兩從的非同步叢集。接下來,我們就看看怎麼去使用RocketMQ,在使用之前,先要在NameServer中建立Topic,我們知道RocketMQ是基於Topic的訊息佇列,在生產者傳送訊息的時候,要指定訊息的Topic,這個Topic的路由規則是怎樣的,這些都要在NameServer中去建立。
Topic的建立
我們先看看Topic的命令是如何使用的,如下:
./bin/mqadmin updateTopic -h
usage: mqadmin updateTopic -b <arg> | -c <arg> [-h] [-n <arg>] [-o <arg>] [-p <arg>] [-r <arg>] [-s <arg>] -t
<arg> [-u <arg>] [-w <arg>]
-b,--brokerAddr <arg> create topic to which broker
-c,--clusterName <arg> create topic to which cluster
-h,--help Print help
-n,--namesrvAddr <arg> Name server address list, eg: 192.168.0.1:9876;192.168.0.2:9876
-o,--order <arg> set topic's order(true|false)
-p,--perm <arg> set topic's permission(2|4|6), intro[2:W 4:R; 6:RW]
-r,--readQueueNums <arg> set read queue nums
-s,--hasUnitSub <arg> has unit sub (true|false)
-t,--topic <arg> topic name
-u,--unit <arg> is unit topic (true|false)
-w,--writeQueueNums <arg> set write queue nums
其中有一段,-b <arg> | -c <arg>
,說明這個Topic可以指定叢集,也可以指定佇列,我們先建立一個Topic指定叢集,因為叢集中有兩個佇列broker-a
和broker-b
,看看我們的訊息是否在兩個佇列中負載;然後再建立一個Topic指向broker-a
,再看看這個Topic的訊息是不是隻在broker-a
中。
建立兩個Topic,
./bin/mqadmin updateTopic -c 'RocketMQ-Cluster' -t cluster-topic -n '192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876'
./bin/mqadmin updateTopic -b 192.168.73.130:10911 -t broker-a-topic
第一個命令建立了一個叢集的Topic,叫做cluster-topic;第二個命令建立了一個只在broker-a中才有的Topic,我們指定了-b 192.168.73.130:10911
,這個是broker-a的地址和埠。
生產者傳送訊息
我們新建SpringBoot專案,然後引入RocketMQ的jar包,
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.3.0</version>
</dependency>
然後配置一下生產者的客戶端,在這裡使用@Configuration
這個註解,具體如下:
@Configuration
public class RocketMQConfig {
@Bean(initMethod = "start",destroyMethod = "shutdown")
public DefaultMQProducer producer() {
DefaultMQProducer producer = new
DefaultMQProducer("DefaultMQProducer");
producer.setNamesrvAddr("192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;");
return producer;
}
}
- 首先建立一個生產者組,名字叫做DefaultMQProducer;
- 然後指定NameServer,192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;
- 最後在
@Bean
註解中指定初始化的方法,和銷燬的方法;
這樣,生產者的客戶端就配置好了,然後再寫個Test類,在Test類中向MQ中傳送訊息,如下,
@SpringBootTest
class RocketmqDemoApplicationTests {
@Autowired
public DefaultMQProducer defaultMQProducer;
@Test
public void producerTest() throws Exception {
for (int i = 0;i<5;i++) {
Message message = new Message();
message.setTopic("cluster-topic");
message.setKeys("key-"+i);
message.setBody(("this is simpleMQ,my NO is "+i).getBytes());
SendResult sendResult = defaultMQProducer.send(message);
System.out.println("SendStatus:" + sendResult.getSendStatus());
System.out.println("BrokerName:" + sendResult.getMessageQueue().getBrokerName());
}
}
}
- 我們先自動注入前面配置DefaultMQProducer;
- 然後在Test方法中,迴圈5次,傳送5個訊息,訊息的Topic指定為cluster-topic,是叢集的訊息,然後再設定訊息的key和內容,最後呼叫send方法傳送訊息,這個send方法是同步方法,程式執行到這裡會阻塞,等待返回的結果;
- 最後,我們列印出返回的結果和broker的名字;
執行一下,看看結果:
SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-b
SendStatus:SEND_OK
BrokerName:broker-a
5個訊息傳送都是成功的,而傳送的佇列有4個是broker-b,1個broker-a,說明兩個broker之間還是有負載的,負載的規則我們猜測是隨機。
我們再寫個測試方法,看看broker-a-topic
這個Topic的傳送結果是什麼樣子的,如下:
@Test
public void brokerTopicTest() throws Exception {
for (int i = 0;i<5;i++) {
Message message = new Message();
message.setTopic("broker-a-topic");
message.setKeys("key-"+i);
message.setBody(("this is broker-a-topic's MQ,my NO is "+i).getBytes());
defaultMQProducer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("SendStatus:" + sendResult.getSendStatus());
System.out.println("BrokerName:" + sendResult.getMessageQueue().getBrokerName());
}
@Override
public void onException(Throwable e) {
e.printStackTrace();
}
});
System.out.println("非同步傳送 i="+i);
}
}
- 訊息的Topic指定的是broker-a-topic,這個Topic我們只指定了broker-a這個佇列;
- 傳送的時候我們使用的是非同步傳送,程式到這裡不會阻塞,而是繼續向下執行,傳送的結果正常或者異常,會呼叫對應的onSuccess和onException方法;
- 我們在onSuccess方法中,列印出傳送的結果和佇列的名稱;
執行一下,看看結果:
非同步傳送 i=0
非同步傳送 i=1
非同步傳送 i=2
非同步傳送 i=3
非同步傳送 i=4
SendStatus:SEND_OK
SendStatus:SEND_OK
SendStatus:SEND_OK
SendStatus:SEND_OK
BrokerName:broker-a
SendStatus:SEND_OK
BrokerName:broker-a
BrokerName:broker-a
BrokerName:broker-a
BrokerName:broker-a
由於我們是非同步傳送,所以最後的日誌先列印了出來,然後列印出返回的結果,都是傳送成功的,並且佇列都是broker-a,完全符合我們的預期。
消費者
生產的訊息已經傳送到了佇列當中,再來看看消費者端如何消費這個訊息,我們在這個配置類中配置消費者,如下:
@Bean(initMethod = "start",destroyMethod = "shutdown")
public DefaultMQPushConsumer pushConsumer() throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("DefaultMQPushConsumer");
consumer.setNamesrvAddr("192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;");
consumer.subscribe("cluster-topic","*");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
if (msgs!=null&&msgs.size()>0) {
for (MessageExt msg : msgs) {
System.out.println(new String(msg.getBody()));
System.out.println(context.getMessageQueue().getBrokerName());
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
} );
return consumer;
}
- 我們建立了一個消費者組,名字叫做DefaultMQPushConsumer;
- 然後指定NameServer叢集,192.168.73.130:9876;192.168.73.131:9876;192.168.73.132:9876;
- 消費者訂閱的Topic,這裡我們訂閱的是cluster-topic,後面的*號是對應的tag,代表我們訂閱所有的tag;
- 最後註冊一個併發執行的訊息監聽器,實現裡邊的consumeMessage方法,在方法中,我們列印出訊息體的內容,和訊息所在的佇列;
- 如果訊息消費成功,返回CONSUME_SUCCESS,如果出現異常等情況,我們要返回RECONSUME_LATER,說明這個訊息還要再次消費;
好了,這個訂閱了cluster-topic的消費者,配置完了,我們啟動一下專案,看看消費的結果如何,
this is simpleMQ,my NO is 2
broker-b
this is simpleMQ,my NO is 3
broker-b
this is simpleMQ,my NO is 1
broker-b
this is simpleMQ,my NO is 0
broker-a
this is simpleMQ,my NO is 4
broker-b
結果符合預期,cluster-topic中的5個訊息全部消費成功,而且佇列是4個broker-b,1個broker-a,和傳送時的結果是一致的。
大家有問題歡迎評論區討論~