今天的博客主题
MQ消息中间件 --》RocketMQ --》RocketMQ进阶(一)
本文主要讲解RocketMQ生产者实现及底层实现原理刨根问底
对于使用原生Java项目来实现的例子就不多说了,官方文档都有
样例地址:https://github.com/apache/rocketmq/blob/master/docs/cn/RocketMQ_Example.md
主要看下springboot是怎么集成RocketMQ,并实现生产者发送消息。
springboot集成rocketmq生产者实现
pom.xml文件引入MQ依赖jar包
<!-- rocketmq依赖 -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.3</version>
</dependency>
application.yml配置文件添加配置
rocketmq:
#服务地址
name-server: 10.10.16.20:9876
#生产者配置
producer:
#生产组名
group: PG_DEMO
#主题
topic: TOPIC_DEMO_ONE
#标签
tag: TAG_ONE
具体代码实现
@RestController
@RequestMapping("/producerDemo")
public class ProducerDemo {
@Resource
private RocketMQTemplate rocketMQTemplate;
@Value(value = "${rocketmq.producer.topic}")
private String topic;
@Value(value = "${rocketmq.producer.tag}")
private String tag;
/**
* 同步发送消息到MQ
*/
@RequestMapping("/syncSendMsg2MQ")
public String syncSendMsg2MQ(){
// 发送的消息体,消息体必须存在
String msg = "Hello World " + LocalDateTime.now();
// 业务主键作为消息key
String key = "123456";
// 发送目的地 TOPIC:TAG
String destination = topic + ":" + tag;
// 构建消息
Message message = MessageBuilder.withPayload(msg)
.setHeader(RocketMQHeaders.KEYS, key)
.build();
SendResult sendResult = rocketMQTemplate.syncSend(destination, message);
if(sendResult.getSendStatus() == SendStatus.SEND_OK){
System.out.println(sendResult.getMsgId() + " : " + sendResult.getSendStatus());
return LocalDateTime.now() +" 》》SUCCESS 》》 " + sendResult.getMsgId() + " : " + sendResult.getSendStatus();
}else{
return LocalDateTime.now() +" 》》 FAIL 》》 " + sendResult.getMsgId() + " : " + sendResult.getSendStatus();
}
}
}
测试
本来是想放在启动类下,但是在实际中不会这么做。就创建个controller,启动项目来真正模拟环境测试下
访问,浏览器返回:2019-07-08T17:12:52.363 》》SUCCESS 》》 0A0A33DB31DC18B4AAC227BE22DD0000 : SEND_OK
编辑器控制台输出:0A0A33DB31DC18B4AAC227BE22DD0000 : SEND_OK
打开rocketmq控制台确定消息是否发送到了服务端。
没问题,可以查到那条消息,而且发送时间一致。
到这里发送者已经集成到springboot里了,并成功发送了消息到服务端。
这也太简单了,5分钟学会应用rocketmq。能应用...当然这些不行的。知己知彼方能百战不殆
下面开始对生产者发送消息进行刨根问底。
rocketmq-spring-boot-starter是对rocketmq的一个封装,不用在像原生java项目那些去写控制生产者消费者的启动销毁。
只需要关注消息体的组成即可,其余的都交给spring来管理了。
看下rocketmq-spring-boot-starter的包结构
这里面做的很简单就是将比较频繁使用到的给封装成Bean,统一由spring管理,当你用的时候直接通过bean方式注入即可。
具体的实现也比较简单,自己也可以实现一个starter的。就不浪费时间细说了,自己找到源码看下。
通过上面的代码块看到了,在发送消息的时候需要将 RocketMQTemplate 通过bean注入的方式给注入进来,就能用了。
RocketMQTemplate的内部结构:
可以看到提供了许多方法,支持不同的消息发送姿势。比如同步发送,异步发送,单向发送,消息延迟发送等等...
值得注意的是在这些方法的参数类型,Message使用的都是Spring的Message,可以说是对spring Message的一个支持,为了是方便从其他MQ能快速切换到RocketMQ。
这个是rocketmq在做starter的时候对消息发送的一个封装。但具体实现发送还需要看底层实现
/**
* Same to {@link #syncSend(String, Message)} with send timeout specified in addition.
* @param destination formats: `topicName:tags`
* @param message {@link org.springframework.messaging.Message}
* @param timeout send timeout with millis
* @param delayLevel level for the delay message
* @return {@link SendResult}
*/
public SendResult syncSend(String destination, Message<?> message, long timeout, int delayLevel) {
// 验证消息是否为空,为空则抛出异常
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) {
log.error("syncSend failed. destination:{}, message is null ", destination);
throw new IllegalArgumentException("`message` and `message.payload` cannot be null");
}
// 消息不为空,发送消息
try {
long now = System.currentTimeMillis();
// message之间的一个转换。SpringMessage To RocketMQMessage
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper,
charset, destination, message);
// 设置消息的延迟发送级别
if (delayLevel > 0) {
rocketMsg.setDelayTimeLevel(delayLevel);
}
// 调用rocketmq-client提供的方法,发送消息
SendResult sendResult = producer.send(rocketMsg, timeout);
// 计算发送时间
long costTime = System.currentTimeMillis() - now;
log.debug("send message cost: {} ms, msgId:{}", costTime, sendResult.getMsgId());
return sendResult;
} catch (Exception e) {
log.error("syncSend failed. destination:{}, message:{} ", destination, message);
throw new MessagingException(e.getMessage(), e);
}
}
继续深入 producer.send()
在深入前先看下这个producer的来头:DefaultMQProducer producer
1. 这个类是发送消息的应用程序入口
2. 对于暴露出去的getter/setter方法都可以调优,默认的配置已适应于大多场景。
3. 这个类里聚合了各种send方法来将消息发送到服务端。每个方法都有利有弊。在编写之前最好先了解它们的优缺点,来自RocketMQ官方的忠告!!!
4. 它是线程安全的。
5. 是MQProducer接口实现类。
public class DefaultMQProducer extends ClientConfig implements MQProducer {
....省略实现代码
}
DefaultMQProducer 所实现的就是MQProducer里的这些。
DefaultMQProducer 里提供了不同的构造函数,来方便实例化它
好!继续深入,这个producer.send() 又调用了 DefaultMQProducerImpl 里的方法,看来 DefaultMQProducerImpl 才是具体实现的位置,不能盲目下那么早的结论,会打脸的...