RocketMQ 知识总结

文章目录

  • 1.RocketMQ安装
  • 2.集群搭建
    • 2.1各角色介绍
    • 2.2 集群类型
    • 2.3 双主双从
  • 3.基本使用案例
  • 4.知识点
    • 4.1消息存储
      • 存储介质
      • 消息的存储和发送
      • 存储结构
      • 刷盘机制
    • 4.2 高可用性机制
      • 消息消费高可用
      • 消息发送高可用
      • 消息主从复制
    • 4.3负载均衡
      • Producer负载均衡
      • Consumer负载均衡
    • 4.4 消息重试
    • 4.5消费幂等

1.RocketMQ安装

  • 前提需要安装配置jdk

  • 下载地址
    http://rocketmq.apache.org/dowloading/releases/

  • 安装

解压后得目录:
RocketMQ 知识总结_第1张图片
bin:启动脚本,包括shell脚本和CMD脚本
conf:实例配置文件 ,包括broker配置文件、logback配置文件等
lib:依赖jar包,包括Netty、commons-lang、FastJSON等

  • 启动NameServer
  • 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
  • 启动Broker
# 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 
  • 测试MQ
  • 发送消息
# 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
  • 关闭RocketMQ
# 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进入控制台界面了

2.集群搭建

2.1各角色介绍

  • Producer:消息的发送者;举例:发信者
  • Consumer:消息接收者;举例:收信者
  • Broker:暂存和传输消息;举例:邮局
  • NameServer:管理Broker;举例:各个邮局的管理机构
  • Topic:区分消息的种类;一个发送者可以发送消息给一个或者多个Topic;一个消息的接收者可以订阅一个或者多个Topic消息
  • Message Queue:相当于是Topic的分区;用于并行发送和接收消息
    RocketMQ 知识总结_第2张图片

2.2 集群类型

集群特点

  • NameServer是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。

  • Broker部署相对复杂,Broker分为MasterSlave,一个Master可以对应多个Slave,但是一个Slave只能对应一个MasterMasterSlave的对应关系通过指定相同的BrokerName,不同的BrokerId来定义,BrokerId0表示Master非0表示SlaveMaster****也可以部署多个。每个BrokerNameServer集群中的所有节点建立长连接,定时注册Topic信息到所有NameServer

  • ProducerNameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServerTopic路由信息,并向提供Topic服务的Master建立长连接,且定时向Master发送心跳。Producer完全无状态,可集群部署。

  • ConsumerNameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServerTopic路由信息,并向提供Topic服务的MasterSlave建立长连接,且定时向MasterSlave发送心跳。Consumer既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。

集群原理
RocketMQ 知识总结_第3张图片

  • Broker每30秒向NameServer发送心跳包,心跳包中包含topic的路由信息
  • NarneServer 收到 Broker 心跳包后 更新 brokerLiveTable 中的信息, 特别记录心跳时间
    lastUpdateTime
  • NarneServer 每隔 10s 扫描 brokerLiveTable, 检 测表中上次收到心跳包的时间,比较当前时间
    与上一次时间,如果超过120s,则认为 broker 不可用,移除路由表中与该 broker相关的所有 信息
  • 消息生产者拉取主题的路由信息,即消息生产者并不会立即感知 Broker 服务器的新增与删除。

conf配置目录中
在这里插入图片描述
分别表示
双主双从异步

  • 优点:即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,同时Master宕机后,消费者仍然可以从Slave消费,而且此过程对应用透明,不需要人工干预,性能同多Master模式几乎一样;
  • 缺点:Master宕机,磁盘损坏情况下会丢失少量消息。

双主双从同步

  • 优点:数据与服务都无单点故障,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高;
  • 缺点:性能比异步复制模式略低(大约低10%左右),发送单个消息的RT会略高,且目前版本在主节点宕机后,备机不能自动切换为主机。

双主

  • 优点:配置简单,单个Master宕机或重启维护对应用无影响,在磁盘配置为RAID10时,即使机器宕机不可恢复情况下,由于RAID10磁盘非常可靠,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢),性能最高;
  • 缺点:单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会受到影响。

2.3 双主双从

A机器上配置broker-a-master和broker-b-slave
机器B上配置broker-b-master和broker-a-slave

  • 配置注意要点

  • listenPort 同一台机器的每个broker端口不一样

  • brokerIP1 云服务器上需要配置外网Ip地址,不然默认内网,访问不了

  • store 存储路径,masterslave的存储路径一样会冲突,反正我的会。

  • 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
  • 配置完成后启动
  • 启动NameServe集群,每台机器上启动就好了 nohup sh mqnamesrv &
  • 启动Broker集群:
  • 就是使用配置文件运行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 &
  • 每台机器都启动一主一从

  • 启动后jps查看启动结果显示一个Namesrv和两个broker
    RocketMQ 知识总结_第4张图片

  • 使用集群监控平台,先拉取代码,然后修改配置文件中的nameSrv集群配置,打包,上传的服务器中启动

RocketMQ 知识总结_第5张图片RocketMQ 知识总结_第6张图片

  • 访问ip:8080

RocketMQ 知识总结_第7张图片
集群工作流程

  1. 启动NameServer,NameServer起来后监听端口,等待Broker、Producer、Consumer连上来,相当于一个路由控制中心。
  2. Broker启动,跟所有的NameServer保持长连接,定时发送心跳包。心跳包中包含当前Broker信息(IP+端口等)以及存储所有Topic信息。注册成功后,NameServer集群中就有Topic跟Broker的映射关系。
  3. 收发消息前,先创建Topic,创建Topic时需要指定该Topic要存储在哪些Broker上,也可以在发送消息时自动创建Topic。
  4. Producer发送消息,启动时先跟NameServer集群中的其中一台建立长连接,并从NameServer中获取当前发送的Topic存在哪些Broker上,轮询从队列列表中选择一个队列,然后与队列所在的Broker建立长连接从而向Broker发消息。
  5. Consumer跟Producer类似,跟其中一台NameServer建立长连接,获取当前订阅Topic存在哪些Broker上,然后直接跟Broker建立连接通道,开始消费消息。

3.基本使用案例

  • client包导入
   <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

4.知识点

4.1消息存储

存储介质

  • 关系型数据库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消息的存储是由ConsumeQueueCommitLog配合完成的。

  • ConsumeQueue:

     是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。
     每个Topic下的个Message Queue都有一个对应的ConsumeQueue文件。
    
  • CommitLog: 消息真正的物理存储文件。

刷盘机制

RocketMQ的消息是存储到磁盘上的,这样既能保证断电后恢复,又可以让存储的消息量超出内存的限制。RocketMQ为了提高性能,会尽可能地保证磁盘的顺序写。消息在通过Producer发送给RocketMQ的时候,有两种写磁盘方式,分布式同步刷盘异步刷盘

RocketMQ 知识总结_第8张图片

  • 同步刷盘

     在返回写成功状态时,消息已经被写入磁盘。
     具体流程是,
     消息写入内存的缓存中后,立刻通知刷盘线程刷盘
     然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程返回消息写成功的状态
    
  • 异步刷盘

     在返回写成功状态时,消息只是被写入了内存的缓存中,写操作的返回快。吞吐量大
     当内存里的消息量积累到一定程度时,统一触发写磁盘动作,快速写入。
    
  • 配置

     同步刷盘还是异步刷盘,都是通过Broker配置文件里的flushDiskType参数设置的
     这个参数被配置成SYNC_FLUSH、ASYNC_FLUSH中的一个。
    

4.2 高可用性机制

RocketMQ分布式集群是通过Master和Slave的配合达到高可用性的。

RocketMQ 知识总结_第9张图片

消息消费高可用

在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 上

  有同步和异步两种复制方式。
  1. 同步复制

    同步复制方式是等Master和Slave均写 成功后才反馈给客户端写成功状态;

    在同步复制方式下,如果Master出故障, Slave上有全部的备份数据,容易恢复,但是同步复制会增大数据写入 延迟,降低系统吞吐量。

  2. 异步复制

    异步复制方式是只要Master写成功 即可反馈给客户端写成功状态。

    在异步复制方式下,系统拥有较低的延迟和较高的吞吐量,但是如果Master出了故障,有些数据因为没有被写 入Slave,有可能会丢失;

  3. 配置

    同步复制和异步复制是通过Broker配置文件里的brokerRole参数进行设置的,这个参数可以被设置成ASYNC_MASTER、 SYNC_MASTER、SLAVE三个值中的一个。

4.3负载均衡

Producer负载均衡

Producer端,每个实例在发消息的时候,默认会轮询所有的message queue发送,以达到让消息平均落在不同的queue上。而由于queue可以散落在不同的broker,所以消息就发送到不同的broker下,如下图:
RocketMQ 知识总结_第10张图片

Consumer负载均衡

  • 1)集群模式

    在集群消费模式下,每条消息只需要投递到订阅这个topic的Consumer Group下的一个实例即可。RocketMQ采用主动拉取的方式拉取并消费消息,在拉取的时候需要明确指定拉取哪一条message queue。而每当实例的数量有变更,都会触发一次所有实例的负载均衡,这时候会按照queue的数量和实例的数量平均分配queue给每个实例。
    默认的分配算法是AllocateMessageQueueAveragely平均分配,如下图:
    RocketMQ 知识总结_第11张图片

  • 2)广播模式

    由于广播模式下要求一条消息需要投递到一个消费组下面所有的消费者实例,所以也就没有消息被分摊消费的说法。

    在实现上,其中一个不同就是在consumer分配queue的时候,所有consumer都分到所有queue。

4.4 消息重试

  • 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 控制台重新发送该消息,让消费者重新消费一次。

4.5消费幂等

消费幂等操作:对相同消息的重复消费需要保持结果是一致的,不会因为多次点击而产生了副作用

  • 1)重复消息的产生

    在互联网应用中,尤其在网络不稳定的情况下,消息队列 RocketMQ 的消息有可能会出现重复,这个重复简单可以概括为以下情况:

    • 发送时消息重复

      当一条消息已被成功发送到服务端并完成持久化,此时出现了网络闪断或者客户端宕机,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

    • 消费时消息重复

      消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次,消息队列 RocketMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

    • 负载均衡时消息重复(包括但不限于网络抖动、Broker 重启以及订阅方应用重启)

      当消息队列 RocketMQ 的 Broker 或客户端重启、扩容或缩容时,会触发 Rebalance,此时消费者可能会收到重复消息。

  • 2)解决方案

    最好的方式是以业务唯一标识作为幂等处理的关键依据,而业务的唯一标识可以通过消息 Key 进行设置。
    比如可以为消息设置唯一的属性key,在消费者消费后使用redis设置(key,message)存储消费记录。收到重复消息时判断redis中是否一消费过了。消费过了则不重复操作。

你可能感兴趣的:(MQ,队列)