前提需要安装配置jdk
下载地址
http://rocketmq.apache.org/dowloading/releases/
安装
解压后得目录:
bin:启动脚本,包括shell脚本和CMD脚本
conf:实例配置文件 ,包括broker配置文件、logback配置文件等
lib:依赖jar包,包括Netty、commons-lang、FastJSON等
RocketMQ默认的虚拟机内存较大,启动Broker如果因为内存不足失败,需要编辑配置文件,修改JVM内存大小
# 编辑runbroker.sh和runserver.sh修改默认JVM大小
vi runbroker.sh
vi runserver.sh
参考配置修改虚拟机内存大小
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
# 1.启动NameServer
nohup sh bin/mqnamesrv &
# 云启动
nohup sh bin/mqnamesrv -n 公网IP:9876 &
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/namesrv.log
# 1.启动Broker
nohup sh bin/mqbroker -n localhost:9876 &
云启动
开放端口9876
非vip通道端口:10911
vip通道端口:10909
10909是VIP通道对应的端口,在JAVA中的消费者对象或者是生产者对象中关闭VIP通道即可无需开放10909端口
#在conf/broker.conf 中 加入 brokerIP1=你的公网IP
nohup sh bin/mqbroker -n 公网ip:9876 -c conf/broker.conf autoCreateTopicEnable=true &
# 2.查看启动日志
tail -f ~/logs/rocketmqlogs/broker.log
# 1.设置环境变量
export NAMESRV_ADDR=ip:9876
# 2.使用安装包的Demo发送消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer
# 1.设置环境变量
export NAMESRV_ADDR=ip:9876
# 2.接收消息
sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer
# 1.关闭NameServer
sh bin/mqshutdown namesrv
# 2.关闭Broker
sh bin/mqshutdown broker
控制台管理
https://github.com/apache/rocketmq-externals,进行打包
git clone https://github.com/apache/rocketmq-externals
cd rocketmq-console
mvn clean package -Dmaven.test.skip=true
注意:打包前在rocketmq-console中配置namesrv集群地址和启动端口号
rocketmq.config.namesrvAddr=192.168.25.135:9876;192.168.25.138:9876
java -jar rocketmq-console-ng-1.0.0.jar
http://ip:8080
进入控制台界面了集群特点
NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
Broker部署相对复杂,Broker分为Master与Slave,一个Master可以对应多个Slave,但是一个Slave只能对应一个Master,Master与Slave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId为0表示Master,非0表示Slave。Master****也可以部署多个。每个Broker与NameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer。
Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。
Consumer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer取Topic路由信息,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
双主双从同步
双主
A机器上配置broker-a-master和broker-b-slave
机器B上配置broker-b-master和broker-a-slave
配置注意要点
listenPort
同一台机器的每个broker端口不一样
brokerIP1
云服务器上需要配置外网Ip地址,不然默认内网,访问不了
store
存储路径,master
和slave
的存储路径一样会冲突,反正我的会。
A机器上配置
配置broker-a.properties
# Licensed to the Apache Software Foundation (ASF) under one or more
#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker-a
#0 表示 Master,>0 表示 Slave
brokerId=0
#nameServer地址,分号分割
namesrvAddr=101.37.30.142:9876;193.112.249.143:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker对外服务的监听端口,这个要配置,同台机器每个broker的端口都不一样
listenPort=10911
#ip地址,云上配置的的话需要配置上外网Ip,不然默认是内网ip访问不了
brokerIP1=101.37.30.142
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/home/rocketmq-all-4.7.0-bin-release/master/store
#commitLog 存储路径
storePathCommitLog=/home/rocketmq-all-4.7.0-bin-release/master/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/home/rocketmq-all-4.7.0-bin-release/master/store/consumequeue
#消息索引存储路径
storePathIndex=/home/rocketmq-all-4.7.0-bin-release/master/store/index
#checkpoint 文件存储路径
storeCheckpoint=/home/rocketmq-all-4.7.0-bin-release/master/store/checkpoint
#abort 文件存储路径
abortFile=/home/rocketmq-all-4.7.0-bin-release/master/store/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SYNC_MASTER
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=SYNC_FLUSH
#开启属性过滤
enablePropertyFilter=true
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
broker-b-s.properties
# Licensed to the Apache Software Foundation (ASF) under one or more
#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker-b
#0 表示 Master,>0 表示 Slave
brokerId=1
#nameServer地址,分号分割
namesrvAddr=101.37.30.142:9876;193.112.249.143:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=10921
#Ip地址
brokerIP1=101.37.30.142
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/home/rocketmq-all-4.7.0-bin-release/slave/store
#commitLog 存储路径
storePathCommitLog=/home/rocketmq-all-4.7.0-bin-release/slave/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/home/rocketmq-all-4.7.0-bin-release/slave/store/consumequeue
#消息索引存储路径
storePathIndex=/home/rocketmq-all-4.7.0-bin-release/slave/store/index
#checkpoint 文件存储路径
storeCheckpoint=/home/rocketmq-all-4.7.0-bin-release/slave/store/checkpoint
#abort 文件存储路径
abortFile=/home/rocketmq-all-4.7.0-bin-release/slave/store/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SLAVE
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=SYNC_FLUSH
#开启属性过滤
enablePropertyFilter=true
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
配置机器B
配置broker-b.properties
# Licensed to the Apache Software Foundation (ASF) under one or more
#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker-b
#0 表示 Master,>0 表示 Slave
brokerId=0
#nameServer地址,分号分割
namesrvAddr=101.37.30.142:9876;193.112.249.143:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=10911
#ip地址
brokerIP1=193.112.249.143
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/home/rocketmq-all-4.7.0-bin-release/master/store
#commitLog 存储路径
storePathCommitLog=/home/rocketmq-all-4.7.0-bin-release/master/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/home/rocketmq-all-4.7.0-bin-release/master/store/consumequeue
#消息索引存储路径
storePathIndex=/home/rocketmq-all-4.7.0-bin-release/master/store/index
#checkpoint 文件存储路径
storeCheckpoint=/home/rocketmq-all-4.7.0-bin-release/master/store/checkpoint
#abort 文件存储路径
abortFile=/home/rocketmq-all-4.7.0-bin-release/master/store/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SYNC_MASTER
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=SYNC_FLUSH
#开启属性过滤
enablePropertyFilter=true
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
broker-a-s.properties
# Licensed to the Apache Software Foundation (ASF) under one or more
#所属集群名字
brokerClusterName=rocketmq-cluster
#broker名字,注意此处不同的配置文件填写的不一样
brokerName=broker-a
#0 表示 Master,>0 表示 Slave
brokerId=1
#nameServer地址,分号分割
namesrvAddr=101.37.30.142:9876;193.112.249.143:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许 Broker 自动创建Topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组,建议线下开启,线上关闭
autoCreateSubscriptionGroup=true
#Broker 对外服务的监听端口
listenPort=10921
#ip地址
brokerIP1=193.112.249.143
#删除文件时间点,默认凌晨 4点
deleteWhen=04
#文件保留时间,默认 48 小时
fileReservedTime=120
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条,根据业务情况调整
mapedFileSizeConsumeQueue=300000
#destroyMapedFileIntervalForcibly=120000
#redeleteHangedFileInterval=120000
#检测物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#存储路径
storePathRootDir=/home/rocketmq-all-4.7.0-bin-release/slave/master/store
#commitLog 存储路径
storePathCommitLog=/home/rocketmq-all-4.7.0-bin-release/slave/store/commitlog
#消费队列存储路径存储路径
storePathConsumeQueue=/home/rocketmq-all-4.7.0-bin-release/slave/store/consumequeue
#消息索引存储路径
storePathIndex=/home/rocketmq-all-4.7.0-bin-release/slave/store/index
#checkpoint 文件存储路径
storeCheckpoint=/home/rocketmq-all-4.7.0-bin-release/slave/store/checkpoint
#abort 文件存储路径
abortFile=/home/rocketmq-all-4.7.0-bin-release/slave/store/abort
#限制的消息大小
maxMessageSize=65536
#flushCommitLogLeastPages=4
#flushConsumeQueueLeastPages=2
#flushCommitLogThoroughInterval=10000
#flushConsumeQueueThoroughInterval=60000
#Broker 的角色
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SLAVE
#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=SYNC_FLUSH
#开启属性过滤
enablePropertyFilter=true
#checkTransactionMessageEnable=false
#发消息线程池数量
#sendMessageThreadPoolNums=128
#拉消息线程池数量
#pullMessageThreadPoolNums=128
nohup sh mqnamesrv &
就是使用配置文件运行bin目录下脚本文件,启动日志输出到指定文件,需要提前创建好文件
nohup sh /home/rocketmq-all-4.7.0-bin-release/bin/mqbroker -c /home/rocketmq-all-4.7.0-bin-release/conf/2m-2s-sync/broker-a-s.properties > /home/rocketmq-all-4.7.0-bin-release/broker-a-s.log 2>&1 &
ip
:8080 <dependency>
<groupId>org.apache.rocketmqgroupId>
<artifactId>rocketmq-clientartifactId>
<version>4.5.1version>
dependency>
consumer.setMessageModel(MessageModel.BROADCASTING);
MessageModel.BROADCASTING
:广播模式MessageModel.BROADCASTING
:负载均衡模式 //1.创建消费者Consumer,制定消费者组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
//2.指定Nameserver地址
consumer.setNamesrvAddr("101.37.30.142:9876;193.112.249.143:9876");
//3.订阅主题Topic和Tag
consumer.subscribe("base1", "*");
//设定消费模式:负载均衡|广播模式
consumer.setMessageModel(MessageModel.BROADCASTING);
//4.设置回调函数,处理消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
//接受消息内容
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.println("consumeThread=" + Thread.currentThread().getName() + "," + new String(msg.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者consumer
consumer.start();
//1.创建消息生产者producer,并制定生产者组名
DefaultMQProducer producer = new DefaultMQProducer("group1");
//2.指定Nameserver地址
producer.setNamesrvAddr("101.37.30.142:9876;193.112.249.143:9876");
//3.启动producer
producer.start();
for (int i = 0; i < 10; i++) {
//4.创建消息对象,指定主题Topic、Tag和消息体
/**
* 参数一:消息主题Topic
* 参数二:消息Tag
* 参数三:消息内容
*/
Message msg = new Message("base1", "Tag1", ("Hello World" + i).getBytes());
//5.发送消息
SendResult result = producer.send(msg);
//发送状态
SendStatus status = result.getSendStatus();
System.out.println("发送结果:" + result);
//线程睡1秒
TimeUnit.SECONDS.sleep(1);
}
//6.关闭生产者producer
producer.shutdown();
springboot
以及更多案例:码云地址:https://gitee.com/mre_chen_wenhao/RocketMQ-Demo.git
关系型数据库DB
Apache下开源的另外一款MQ—ActiveMQ(默认采用的KahaDB做消息存储)可选用JDBC的方式来做消息持久化,通过简单的xml配置信息即可实现JDBC消息存储。由于,普通关系型数据库(如Mysql)在单表数据量达到千万级别的情况下,其IO读写性能往往会出现瓶颈。在可靠性方面,该种方案非常依赖DB,如果一旦DB出现故障,则MQ的消息就无法落盘存储会导致线上故障
文件系统
目前业界较为常用的几款产品(RocketMQ/Kafka/RabbitMQ)均采用的是消息刷盘至所部署虚拟机/物理机的文件系统来做持久化(刷盘一般可以分为异步刷盘和同步刷盘两种模式)。消息刷盘为消息存储提供了一种高效率、高可靠性和高性能的数据持久化方式。除非部署MQ机器本身或是本地磁盘挂了,否则一般是不会出现无法持久化的故障问题。
消息存储
磁盘如果使用得当,磁盘的速度完全可以匹配上网络 的数据传输速度。目前的高性能磁盘,顺序写速度可以达到600MB/s, 超过了一般网卡的传输速度。但是磁盘随机写的速度只有大概100KB/s,和顺序写的性能相差6000倍!因为有如此巨大的速度差别,好的消息队列系统会比普通的消息队列系统速度快多个数量级。RocketMQ的消息用顺序写,保证了消息存储的速度。
消息发送
Linux操统分为【用户态】和【内核态】,文件操作、网络操作需要涉及这两种形态的切换,免不了进行数据复制。
一台服务器 把本机磁盘文件的内容发送到客户端,一般分为两个步骤:
1) read: 读取本地文件内容。
2) write: 将读取的内容通过网络发送出去。
这两个看似简单的操作,实际进行了4 次数据复制,分别是:
1. 从磁盘复制数据到内核态内存;
2. 从内核态内存复 制到用户态内存;
3. 然后从用户态 内存复制到网络驱动的内核态内存;
4. 最后是从网络驱动的内核态内存复 制到网卡中进行传输。
通过使用mmap的方式,可以省去向用户态的内存复制,提高速度。
这种机制在Java中是通过MappedByteBuffer实现的
RocketMQ充分利用了上述特性,也就是所谓的“零拷贝”技术,提高消息存盘和网络发送的速度。
这里需要注意的是,采用MappedByteBuffer这种内存映射的方式有几个限制,
其中之一是一次只能映射1.5~2G 的文件至用户态的虚拟内存,
这也是为何RocketMQ默认设置单个CommitLog日志数据文件为1G的原因了
RocketMQ
消息的存储是由ConsumeQueue
和CommitLog
配合完成的。
ConsumeQueue
:
是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。
每个Topic下的个Message Queue都有一个对应的ConsumeQueue文件。
CommitLog
: 消息真正的物理存储文件。
RocketMQ
的消息是存储到磁盘上的,这样既能保证断电后恢复,又可以让存储的消息量超出内存的限制。RocketMQ
为了提高性能,会尽可能地保证磁盘的顺序写。消息在通过Producer
发送给RocketMQ
的时候,有两种写磁盘方式,分布式同步刷盘
和异步刷盘
同步刷盘
在返回写成功状态时,消息已经被写入磁盘。
具体流程是,
消息写入内存的缓存中后,立刻通知刷盘线程刷盘
然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程返回消息写成功的状态
异步刷盘
在返回写成功状态时,消息只是被写入了内存的缓存中,写操作的返回快。吞吐量大
当内存里的消息量积累到一定程度时,统一触发写磁盘动作,快速写入。
配置
同步刷盘还是异步刷盘,都是通过Broker配置文件里的flushDiskType参数设置的
这个参数被配置成SYNC_FLUSH、ASYNC_FLUSH中的一个。
RocketMQ分布式集群是通过Master和Slave的配合达到高可用性的。
在Consumer的配置文件中,并不需要设置是从Master读还是从Slave 读,当Master不可用
或者繁忙的时候,Consumer会被自动切换到从Slave 读。有了自动切换Consumer这种机制,
当一个Master角色的机器出现故障后,Consumer仍然可以从Slave读取消息,不影响Consumer
程序。这就达到了消费端的高可用性。
在创建Topic的时候,把Topic的多个Message Queue创建在多个Broker组上(相同Broker名称,不同 brokerId的机器组成一个Broker组),这样当一个Broker组的Master不可 用后,其他组的Master仍然可用,Producer仍然可以发送消息。 RocketMQ目前还不支持把Slave自动转成Master,如果机器资源不足, 需要把Slave转成Master,则要手动停止Slave角色的Broker,更改配置文 件,用新的配置文件启动Broker。
如果一个Broker组有Master和Slave,消息需要从Master复制到Slave 上
有同步和异步两种复制方式。
同步复制
同步复制方式是等Master和Slave均写 成功后才反馈给客户端写成功状态;
在同步复制方式下,如果Master出故障, Slave上有全部的备份数据,容易恢复,但是同步复制会增大数据写入 延迟,降低系统吞吐量。
异步复制
异步复制方式是只要Master写成功 即可反馈给客户端写成功状态。
在异步复制方式下,系统拥有较低的延迟和较高的吞吐量,但是如果Master出了故障,有些数据因为没有被写 入Slave,有可能会丢失;
配置
同步复制和异步复制是通过Broker配置文件里的brokerRole参数进行设置的,这个参数可以被设置成ASYNC_MASTER、 SYNC_MASTER、SLAVE三个值中的一个。
Producer端,每个实例在发消息的时候,默认会轮询所有的message queue发送,以达到让消息平均落在不同的queue上。而由于queue可以散落在不同的broker,所以消息就发送到不同的broker下,如下图:
1)集群模式
在集群消费模式下,每条消息只需要投递到订阅这个topic的Consumer Group下的一个实例即可。RocketMQ采用主动拉取的方式拉取并消费消息,在拉取的时候需要明确指定拉取哪一条message queue。而每当实例的数量有变更,都会触发一次所有实例的负载均衡,这时候会按照queue的数量和实例的数量平均分配queue给每个实例。
默认的分配算法是AllocateMessageQueueAveragely平均分配,如下图:
2)广播模式
由于广播模式下要求一条消息需要投递到一个消费组下面所有的消费者实例,所以也就没有消息被分摊消费的说法。
在实现上,其中一个不同就是在consumer分配queue的时候,所有consumer都分到所有queue。
1)顺序消息的重试
对于顺序消息,当消费者消费消息失败后,消息队列 RocketMQ 会自动不断进行消息重试(每次间隔时间为 1 秒),这时,应用会出现消息消费被阻塞的情况。因此,在使用顺序消息时,务必保证应用能够及时监控并处理消费失败的情况,避免阻塞现象的发生。
2)无序消息的重试
对于无序消息(普通、定时、延时、事务消息),当消费者消费消息失败时,可以通过设置返回状态达到消息重试的结果。
无序消息的重试只针对集群消费方式生效;广播方式不提供失败重试特性,即消费失败后,失败消息不再重试,继续消费新的消息。
3)重试次数
消息队列 RocketMQ 默认允许每条消息最多重试 16 次,每次重试的间隔时间如下:
第几次重试 | 与上次重试的间隔时间 | 第几次重试 | 与上次重试的间隔时间 |
---|---|---|---|
1 | 10 秒 | 9 | 7 分钟 |
2 | 30 秒 | 10 | 8 分钟 |
3 | 1 分钟 | 11 | 9 分钟 |
4 | 2 分钟 | 12 | 10 分钟 |
5 | 3 分钟 | 13 | 20 分钟 |
6 | 4 分钟 | 14 | 30 分钟 |
7 | 5 分钟 | 15 | 1 小时 |
8 | 6 分钟 | 16 | 2 小时 |
notic
如果消息重试 16 次后仍然失败,消息将不再投递。如果严格按照上述重试时间间隔计算,某条消息在一直消费失败的前提下,将会在接下来的 4 小时 46 分钟之内进行 16 次重试,超过这个时间范围消息将不再重试投递。
注意: 一条消息无论重试多少次,这些重试消息的 Message ID 不会改变。
4)重试相关配置
不重试
Action.CommitMessage
重试
1.返回 Action.ReconsumeLater (推荐)
2.返回 Null
3.抛出异常
设置重试次数
Properties properties = new Properties();
properties.put(PropertyKeyConst.MaxReconsumeTimes,"20");
Consumer consumer =ONSFactory.createConsumer(properties);
消息最大重试次数的设置对相同 Group ID 下的所有 Consumer 实例有效。
如果只对相同 Group ID 下两个 Consumer 实例中的其中一个设置了MaxReconsumeTimes
那么该配置对两个 Consumer 实例均生效。
配置采用覆盖的方式生效,即最后启动的 Consumer 实例会覆盖之前的启动实例的配置
public Action consume(Message message, ConsumeContext context) {
//处理消息
doConsumeMessage(message);
//方式1:返回 Action.ReconsumeLater,消息将重试
return Action.ReconsumeLater;
//方式2:返回 null,消息将重试
return null;
//方式3:直接抛出异常, 消息将重试
throw new RuntimeException("Consumer Message exceotion");
}
public Action consume(Message message, ConsumeContext context) {
try {
//获取消息的重试次数
System.out.println(message.getReconsumeTimes());
doConsumeMessage(message);
} catch (Throwable e) {
//捕获消费逻辑中的所有异常,并返回 Action.CommitMessage;
return Action.CommitMessage;
}
//消息处理正常,直接返回 Action.CommitMessage;
return Action.CommitMessage;
}
5)死信队列
当一条消息初次消费失败,消息队列 RocketMQ 会自动进行消息重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列 RocketMQ 不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中。
在消息队列 RocketMQ 中,这种正常情况下无法被消费的消息称为死信消息(Dead-Letter Message),存储死信消息的特殊队列称为死信队列(Dead-Letter Queue)。
一条消息进入死信队列,意味着某些因素导致消费者无法正常消费该消息,因此,通常需要对其进行特殊处理。排查可疑因素并解决问题后,可以在消息队列 RocketMQ 控制台重新发送该消息,让消费者重新消费一次。
消费幂等操作:对相同消息的重复消费需要保持结果是一致的,不会因为多次点击而产生了副作用
1)重复消息的产生
在互联网应用中,尤其在网络不稳定的情况下,消息队列 RocketMQ 的消息有可能会出现重复,这个重复简单可以概括为以下情况:
发送时消息重复
当一条消息已被成功发送到服务端并完成持久化,此时出现了网络闪断或者客户端宕机,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。
消费时消息重复
消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次,消息队列 RocketMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。
负载均衡时消息重复(包括但不限于网络抖动、Broker 重启以及订阅方应用重启)
当消息队列 RocketMQ 的 Broker 或客户端重启、扩容或缩容时,会触发 Rebalance,此时消费者可能会收到重复消息。
2)解决方案
最好的方式是以业务唯一标识作为幂等处理的关键依据,而业务的唯一标识可以通过消息 Key 进行设置。
比如可以为消息设置唯一的属性key,在消费者消费后使用redis设置(key,message)存储消费记录。收到重复消息时判断redis中是否一消费过了。消费过了则不重复操作。