一、看前提问:
1、RocketMQ支持的消息类型有哪些?
2、RocketMQ各消息类型的特点有哪些?
3、RocketMQ各消息类型的优缺点?
4、RocketMQ各消息类型的实现原理?
5、RocketMQ各消息类型的应用场景?
备注:本章旨要从概念上介绍各个类型的消息,详细的请看对应的消息源码解析篇
二、消息类型:
1、普通消息:
概念:
普通消息是 RocketMQ 版中⽆特性的消息。 普通消息主要包含了:同步消息、异步消息两种。
1.1、同步消息的发送:
原理:
同步消息就是生产者(消息发送方)发送一条消息后必须要收到服务端返回响应后才发下一条消息。
应用场景:
重要消息例如邮件发送等;
1.2、异步消息:
概念:
需要⽤户实现异步发送回调接⼝(SendCallback)
原理:
应用场景:
异步发送⼀般⽤于链路耗时较⻓,对响应时间较为敏感的业务场景
1.3、单向发送消息:
概念:
发送⽅只负责发送消息,不等待服务端返回响应且没有回调函数触发,即只发送请求不等待应答。响应毫米级
原理:
应用场景:
需要极快的响应速度,但不能保证可靠性
2、顺序消息:
概念:
顺序消息(FIFO 消息)是消息队列,顺序发布和顺序消费是指对于指定的⼀个 Topic⽣产者按照⼀定的先后顺序发布消息;消费者按照既定的先后顺序订阅消息,即先发布的消息⼀定会先被客户端接收到。
如何保证顺序:
-
消息被发送时保持顺序
-
消息被存储时保持和发送的顺序⼀致
-
消息被消费时保持和存储的顺序⼀致
理解:
只保证消息从头到尾的数据是按顺序来的 至于多个消息之间的顺序不保证
顺序实现的方式: -
全局顺序:对于指定的⼀个 Topic,所有消息按照严格的先⼊先出
-
分区顺序:对于指定的⼀个 Topic,所有消息根据 Sharding Key 进⾏区块分区。 同⼀个分区内的消息按照严格的 FIFO 顺序进⾏发布和消费
MessageListenerOrderly与MessageListenerConcurrently区别: -
MessageListenerOrderly:有序消费,同⼀队列的消息同⼀时刻只能⼀个线程消费,可保证消息在同⼀队列严格有序消费
-
MessageListenerConcurrently:并发消费
3、广播消息:
**3.1:类型:**集群模式、广播模式
3.2:集群模式:
RocketMQ默认为集群模式,集群模式下每条消息只会被处理(消费)一次且消费进度在服务端维护可靠性更高。
注意: -
集群消费模式下,每条消息只会被分发到一台服务器上处理。
-
集群消费模式下,不会保证失败的同一条消息在重新投放的时候会被路由到同一台服务器上。
3.2:广播模式:
每条消息都需要被集群下的每一个消费者处理。
注意: -
广播模式下不支持顺序消息
-
不支持重置消费点位
-
每条消息都需要被相同订阅逻辑的多台机器处理
-
消费进度在客户端维护
-
消息队列 保证每条消息⾄少被每台客户端消费⼀次,但是并不会重投消费失败的消息,因此业务⽅需要关注消费失败的情况
-
客户端每⼀次重启都会从最新消息消费。客户端在被停⽌期间发送⾄服务端的消息将会被⾃动跳过。
-
每条消息都会被⼤量的客户端重复处理所以推荐集群模式
4:延时消息:
**概念:**延迟一段时间后才会进行投放消费
**适用场景:**订单失效
延迟的实现方式: -
轮询
-
延时服务,将延迟消息通过⼀个临时存储进⾏暂存,到期后才投递到⽬标Topic中
注意: -
对临时存储需要满足高性能(写入快),高可靠(写入后能备份不能丢失),支持排序,长时间保存
-
RocketMQ不支持任意时间的延迟只支持以下任意时间的延迟但可以自行增加延迟级别
#例如:你想⽀持1天的延迟,修改最后⼀个level的值为1d
String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
原理:
RocketMQ延迟消息只能存在指定的topic(SCHEDULE_TOPIC_XXXX),注意根据延迟level的个数,创建对应数量的队列并不一定是在内部创建固定数量队列而是在各个节点下都会有相同数量的队列。
#生产者发送延迟消息很简单
Message msg=new Message();
msg.setTopic("topic");
msg.setTags("tag");
msg.setBody("delay message".getBytes());
//设置延迟level为6,对应延迟2分钟
msg.setDelayTimeLevel(6);
producer.send(msg);
流转图:
注意:消息从commitlog转发到consumerqQueue是异步进行的,以下是对延迟消息的特殊处理:
投递时间=消息存储时间(storeTimestamp) + 延迟级别对应的时间
consumerqQueue结构如下:
commitLogOffeset:长度8byte,记录在commitlog中的位置
size:长度4byte,消息的大小
MessageTagHashcode:长度8byte,记录消息的tag值消息过滤时使用,延迟消息记录的是投递时间戳所以这也是什么长度为8.
可以参考源码次此部分
CommitLog#checkMessageAndReturnSize
5.批量消息:
批量消息发送可以提高发送性能
- topic相同
- waitStoreMsgOK相同(⾸先我们建设消息的iswaitstoremsgok=true(默认为true), 如果没有异
常,我们将始终收到"OK",
org.apache.rocketmq.common.message.Message#isWaitStoreMsgOK)) - 不支持延迟发送,一批消息不能大于1M
public class BatchProducer{}
6.过滤消息:
过滤消息类型包括:tag模式过滤、sql表达式过滤、类过滤模式
tag消息过滤:
发送消息时我们会为每⼀条消息设置TAG标签,同⼀⼤类中的消息放在⼀个主题TOPIC下
public class FilterByTagProducer {}
public class FilterByTagConsumer {}
sql表达式过滤:
支持语法:
1. 数值⽐较, 如 > , >= , < , <= , BETWEEN , = ;
2. 字符⽐较, 如 = , <> , IN ;
3. IS NULL or IS NOT NULL ;
4. 逻辑连接符 AND , OR , NOT ;
支持类型:
5. 数值型, 如123, 3.1415;
6. 字符型, 如 ‘abc’, 必须⽤单引号;
7. NULL , 特殊常数;
8. 布尔值, TRUE or FALSE ;
7.事务消息:
- 事务消息:提供类型X/Open XA分布式消息达到最终一致
- 半事务消息:消息发送给服务端未收到二次确认消息会被标记为“暂不能投递”
- 消息回查:由于⽹络闪断、⽣产者应⽤重启等原因,导致某条事务消息的⼆次确认丢失,消息队列服务端通过扫描发现某条消息⻓期处于“半事务消息”时,需要主动向消息⽣产者询问
该消息的最终状态(Commit 或是 Rollback)
交互流程:
注意: - 事务消息不能支持批量和延迟消息
- 单个消息默认检查15次,transactionCheckMax修改此参数可以调整检查次数,检查超过次数Broker会默认丢弃该消息并默认打印错误日志,可重写AbstractTransactionalMessageCheckListener 类来改变。
- 事务消息将在 Broker 配置⽂件中的参数 transactionTimeout 这样的特定时间⻓度之后被检查。当发送事务消息时,⽤户还可以通过设置⽤户属性 CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 transactionTimeout 参数。
- 如果希望确保事务消息不丢失、并且事务完整性得到保证,建议使⽤同步的双重写⼊机制。
- 事务消息生产者id不能与其他类型消息的生产者id共享,事务消息允许反向查询、MQ服务器能通过它们的⽣产者 ID 查询到消费者。
要使⽤RocketMQ的事务消息,要实现⼀个TransactionListener的接⼝
public interface TransactionListener {
/**
* When send transactional prepare(half) message succeed, this method will
be invoked to execute local transaction.
* 执⾏本地事务
* @param msg Half(prepare) message
* @param arg Custom business parameter
* @return Transaction state
*/
LocalTransactionState executeLocalTransaction(final Message msg, final
Object arg);
/**
* When no response to prepare(half) message. broker will send check
message to check the transaction status, and this
* method will be invoked to get local transaction status.
*消息回查后,需要检查对应消息的本地事务执⾏的最终结果
* @param msg Check message
* @return Transaction state
*/
LocalTransactionState checkLocalTransaction(final MessageExt msg);
}
事务消息是两个阶段提交的,消息有两个状态prepared和commited,消息执行send方法后进入prepared状态,之后执行executeLocalTransaction方法该方法有3个返回值
- LocalTransactionState.COMMIT_MESSAGE
- LocalTransactionState.ROLLBACK_MESSAGE
- LocalTransactionState.UNKNOW:看下一段
LocalTransactionState.UNKNOW返回时:此状态消息状态依然为prepared,RocketMQ会每隔⼀段时间调⽤⼀次checkLocalTransaction方法时间为1分钟
/**
* Transaction message check interval.
*/
@ImportantField
private long transactionCheckInterval = 60 * 1000;
#检查多少次BrokerConfig类中
/**
* The maximum number of times the message was checked, if exceed this
value, this message will be discarded.
*/
@ImportantField
private int transactionCheckMax = 15;
END;