RocketMQ4.4.0
最近在做一個小項目的過程中使用了MQ消息隊列。
選擇哪種MQ我也糾結了很久,最後選擇了阿里的開源項目RocketMQ,不過現在已經貢獻給Apache社區了。
但是在使用的過程中也是踩了很多坑的,下面我會把我遇到的一些問題羅列出來。
- No route info of this topic
- 本地連接服務器連接超時,連接失敗
- topic可以創建,可以發送消息,但是沒有被消費
- 一個consumer訂閱多個topic應該怎麼設置。
問題多多,但是都是環境的問題,代碼基本上都是一樣的,所以出了問題首先檢查的就是你的環境是否連接正確。
下面我把我的代碼環境配置羅列一下:
- 消費者Producer
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* rocketMq 生產者包裝
*
* @author zhengjf
* @version 1.0
* @date 2019/3/5
*/
@Service
@Slf4j
public class MqProducerService {
@Value("${rocketmq.name-server}")
private String namesrvAddr;
@Value("${rocketmq.producer.group}")
private String producerGroup;
private DefaultMQProducer producer;
/**
* DefaultMQProducer 普通消息生產者對象創建
*
* @return void
* @author zhengjf
* @date 2019/3/5
*/
@PostConstruct
public void initProducer() {
producer = new DefaultMQProducer(producerGroup);
producer.setNamesrvAddr(namesrvAddr);
producer.setRetryTimesWhenSendFailed(3);
producer.setVipChannelEnabled(false);
try {
producer.start();
log.info("[Producer 已啓動]");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 普通消息 同步發送消息,只要不拋異常就是成功
*
* @param topic, tags, msg
* @return java.lang.String
* @author zhengjf
* @date 2019/3/5
*/
public SendResult send(String topic, String tags, String msg) throws Exception {
Message message = new Message(
// Message所屬的Topic
topic,
// Message Tag,可理解爲mail中的標籤,對消息進行再歸類,方便Consumer指定過濾條件在MQ服務器過濾
tags,
// Message Body,任何二進制形式的數據,MQ不做任何干預,需要Producer與Consumer協商好一致的序列化和反序列化方式
msg.getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult result = producer.send(message);
log.info("發送消息後返回:" + result.toString());
return result;
}
/**
* 延時發送
* RocketMQ目前指定的延時時間間隔有DelayTimeLevel 1s,5s,10s,30s,1m,2m,3m,4m,5m,6m,7m,8m,9m,10m,20m,30m,1h,2h,用等級來表示時間間隔。
*
* @param topic, tags, msg, delayTimeLevel 時間等級(30分鐘是16)
* @return org.apache.rocketmq.client.producer.SendResult
* @author zhengjf
* @date 2019/3/5
*/
public SendResult sendDelayTime(String topic, String tags, String msg, int delayTimeLevel) throws Exception {
Message message = new Message(
// Message所屬的Topic
topic,
// Message Tag,可理解爲mail中的標籤,對消息進行再歸類,方便Consumer指定過濾條件在MQ服務器過濾
tags,
// Message Body,任何二進制形式的數據,MQ不做任何干預,需要Producer與Consumer協商好一致的序列化和反序列化方式
msg.getBytes(RemotingHelper.DEFAULT_CHARSET));
message.setDelayTimeLevel(delayTimeLevel);
SendResult result = producer.send(message);
log.info("發送消息後返回:" + result.toString());
return result;
}
/**
* 這裏用到了這個mq的異步處理,類似ajax,可以得到發送到mq的情況,並做相應的處理
* 不過要注意的是這個是異步的
*
* @param topic, tags, msg
* @return void
* @author zhengjf
* @date 2019/3/5
*/
public String sendAsync(String topic, String tags, String msg) throws Exception {
Message message = new Message(
// Message所屬的Topic
topic,
// Message Tag,可理解爲Gmail中的標籤,對消息進行再歸類,方便Consumer指定過濾條件在MQ服務器過濾
tags,
// Message Body,任何二進制形式的數據,MQ不做任何干預,需要Producer與Consumer協商好一致的序列化和反序列化方式
msg.getBytes(RemotingHelper.DEFAULT_CHARSET));
producer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
log.info("傳輸成功。" + sendResult.toString());
}
@Override
public void onException(Throwable e) {
log.error("傳輸失敗", e);
}
});
// 在callback返回之前。
log.info("send message async." + message.toString());
return message.toString();
}
@PreDestroy
public void shutDownProducer() {
if (producer != null) {
producer.shutdown();
}
}
}
- 消費者監聽
import com.mjs.common.common.OrderStatusEnum;
import com.mjs.mojisishop.entity.OrderInfo;
import com.mjs.mojisishop.mapper.OrderInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* 監聽MQ的主題消息,進行消費。可以監聽多個
*
* @author zhengjf
* @version 1.0
* @date 2019/3/5
*/
@Component
@Slf4j
public class ConsumerListener {
@Value("${rocketmq.name-server}")
private String namesrvAddr;
@Value("${rocketmq.consumer.group}")
private String consumerGroup;
@Autowired
OrderInfoMapper orderInfoMapper;
@PostConstruct
public void defaultMQPushConsumer() throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(consumerGroup);
consumer.setNamesrvAddr(namesrvAddr);
consumer.subscribe("order", "pay");
// 如果是第一次啓動,從隊列頭部開始消費
// 如果不是第一次啓動,從上次消費的位置繼續消費
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
// 開啓內部類實現監聽
consumer.registerMessageListener((MessageListenerConcurrently) (list, context) -> {
try {
for (MessageExt messageExt : list) {
String messageBody = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
log.info("消費者受到消息:[Consumer] msgID(" + messageExt.getMsgId() + ") msgBody : " + messageBody);
}
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.subscribe("test222", "pay");
consumer.registerMessageListener((MessageListenerConcurrently) (list, context) -> {
try {
for (MessageExt messageExt : list) {
String messageBody = new String(messageExt.getBody(), RemotingHelper.DEFAULT_CHARSET);
log.info("消費者受到消息:[Consumer] msgID(" + messageExt.getMsgId() + ") msgBody : " + messageBody);
}
} catch (Exception e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
consumer.start();
log.info("[Consumer 已啓動]");
}
}
就是這麼簡單的兩個文件就可以了。
- pom
my mq.version 4.4.0
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>${mq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-common</artifactId>
<version>${mq.version}</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-remoting</artifactId>
<version>${mq.version}</version>
</dependency>
代碼說完了就說以下我的環境配置。
首先我的mq放在阿里雲服務器上
cd /home/tools/rocketmq-all-4.4.0-bin-release
關閉namesrv服務:
sh bin/mqshutdown namesrv
關閉broker服務 :
sh bin/mqshutdown broker
啓動namesrv服務:
nohup sh bin/mqnamesrv &
啓動broker服務:公網ip
nohup sh bin/mqbroker -c conf/broker.conf &
我的broker.conf 內容有修改,所以上面啓動的時候沒有指定nameserver地址
在默認的配置下面添加兩項
公網ip啊。要不然本地的服務就連接不上mq服務器,沒辦法發送消費消息
#nameServer地址,分號分割
namesrvAddr = 57.111.156.132:9876
brokerIP1 = 57.111.156.132
還有一個要注意的就是如果你本地連接雲服務器的mq,要把本地的防火牆還有云服務的防火牆關了。
如果要查看mq的日誌,一般都放在/root/logs/rocketmqlogs中,這是我的日誌路徑,也可能不同用戶不同地址
有broker.log、namesrv.log。。。。。。。
有問題也可以找我交流一下
471639492