RocketMQ问题
2.RocketMQ由哪些角色组成,每个角色作用和特点是什么?
RocketMQ从消费发送到消费的执行流程
- Producer发送消息到Broker,负载均衡策略默认随机
- Broker接收消息,写入PageCage,返回成功
- Broker刷盘,消息存储Consumer queue、commit log
- Consumer从Broker拉取消息,拉取方式长轮询pull
- Consumer消费消息,处理业务逻辑
- Consumer返回ACK,更新Broker offset
- 消费失败,消息转入失败队列
- Broker的ScheduleService从重试队列拉取消息,重放这个消息
- 重试16次如果还是失败,消息进入死信队列
- 通过RocketMQ操作面板监控死信队列,手动处理
3.RocketMQ Broker中的消息被消费后会立即删除吗?
不会,每条消息都会持久化到CommitLog中,每个consumer连接到broker后会维持消费进度信息,当有消息消费后只是当前consumer的消费进度(CommitLog的offset)更新了。
那么消息会堆积吗?什么时候清理过期消息?
4.6版本默认48小时后会删除不再使用的CommitLog文件。
- 检查这个文件最后访问时间
- 判断是否大于过期时间
- 指定时间删除,默认凌晨4点
RocketMQ消费模式有几种?
消费模型由consumer决定,消费维度为Topic
集群消费,广播消费
集群消费: 一组consumer同时消费一个topic,可以分配消费负载均衡策略分配consumer对应消费topic下的哪些queue
多个group同时消费一个topic时,每个group都会消费到数据
一条消息只会被一个group中的consumer消费,
广播消费: 消息将对一 个Consumer Group 下的各个 Consumer 实例都消费一遍。即使这些 Consumer 属于同一个Consumer Group ,消息也会被 Consumer Group 中的每个 Consumer 都消费一次。
顺序消息缺陷
发送顺序消息无法利用集群Fail Over特性消费顺序消息的并行度依赖于队列数量队列热点问题,个别队列由于哈希不均导致消息过多,消费速度跟不上,产生消息堆积问题遇到消息失败的消息,无法跳过,当前队列消费暂停。
消费消息时使用的是push还是pull?
在刚开始的时候就要决定使用哪种方式消费。两种(都实现了MQConsumerInner接口):
`DefaultLitePullConsumerImpl` 拉
`DefaultMQPushConsumerImpl`推
名称上看起来是一个推,一个拉,但实际底层实现都是采用的**长轮询机制**,即拉取方式broker端属性 longPollingEnable 标记是否开启长轮询。默认开启。
为什么要主动拉取消息而不使用事件监听方式?
事件驱动方式是建立好长连接,由事件(发送数据)的方式来实时推送。
如果broker主动推送消息的话有可能push速度快,消费速度慢的情况,那么就会造成消息在consumer端堆积过多,同时又不能被其他consumer消费的情况。
NameServer实现原理:
NameServer是一个非常简单的Topic路由注册中心,其角色类似Dubbo中的zookeeper,支持Broker的动态注册与发现。主要包括两个功能
Broker管理,NameServer接受Broker集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查Broker是否还存活;
路由信息管理,每个NameServer将保存关于Broker集群的整个路由信息和用于客户端查询的队列信息。然后Producer和Conumser通过NameServer就可以知道整个Broker集群的路由信息,从而进行消息的投递和消费
NameServer通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker是向每一台NameServer注册自己的路由信息,所以每一个NameServer实例上面都保存一份完整的路由信息。当某个NameServer因某种原因下线了,Broker仍然可以向其它NameServer同步其路由信息,Producer,Consumer仍然可以动态感知Broker的路由的信息
NameServer实例之间互不通信,这本身也是其设计亮点之一,即允许不同NameServer之间数据不同步(像Zookeeper那样保证各节点数据强一致性会带来额外的性能消耗)
RocketMQ集群模式:
单Master模式:只有一个 Master节点
优点:配置简单,方便部署
缺点:这种方式风险较大,一旦Broker重启或者宕机时,会导致整个服务不可用,不建议线上环境使用
多Master模式: 一个集群无 Slave,全是 Master,例如 2 个 Master 或者 3 个 Master
优点:配置简单,单个Master 宕机或重启维护对应用无影响,在磁盘配置为RAID10 时,即使机器宕机不可恢复情况下,由与 RAID10磁盘非常可靠,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢)。性能最高。多 Master 多 Slave 模式,异步复制
缺点:单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会受到受到影响
多Master多Slave模式(异步复制):每个 Master 配置一个 Slave,有多对Master-Slave, HA,采用异步复制方式,主备有短暂消息延迟,毫秒级。
优点:即使磁盘损坏,消息丢失的非常少,且消息实时性不会受影响,因为Master 宕机后,消费者仍然可以从 Slave消费,此过程对应用透明。不需要人工干预。性能同多 Master 模式几乎一样。
缺点: Master 宕机,磁盘损坏情况,会丢失少量消息。
多Master多Slave模式(同步双写):每个 Master 配置一个 Slave,有多对Master-Slave, HA采用同步双写方式,主备都写成功,向应用返回成功。
优点:数据与服务都无单点, Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高
缺点:性能比异步复制模式略低,大约低 10%左右,发送单个消息的 RT会略高。目前主宕机后,备机不能自动切换为主机。
broker如何处理拉取请求的?
consumer首次请求broker,broker中是否有符合条件的消息
有 -> 响应consumer,等待下次consumer的请求
没有->挂起consumer的请求,即不断开连接,也不返回数据
挂起时间长短,长轮询写死,短轮询可以配
使用consumer的offset,DefaultMessageStore#ReputMessageService#run方法:每隔1ms检查commitLog中是否有新消息,有的话写入到pullRequestTable,当有新消息的时候返回请求,PullRequestHoldService 来Hold连接,每个5s执行一次检查pullRequestTable有没有消息,有的话立即推送。
RocketMQ如何做负载均衡?
通过Topic在多broker种分布式存储实现。
producer端
发送端指定Target message queue发送消息到相应的broker,来达到写入时的负载均衡
- 提升写入吞吐量,当多个producer同时向一个broker写入数据的时候,性能会下降
- 消息分布在多broker种,为负载消费做准备
每 30 秒从 nameserver获取 Topic 跟 Broker 的映射关系,近实时获取最新数据存储单元,queue落地在哪个broker中
在使用api中send方法的时候,可以指定Target message queue写入或者使用MessageQueueSelector
默认策略是随机选择:
- producer维护一个index
- 每次取节点会自增
- index向所有broker个数取余
- 自带容错策略
consumer端
客户端完成负载均衡
- 获取集群其他节点
- 当前节点消费哪些queue
- **负载粒度直到Message Queue**
- **consumer的数量最好和Message Queue的数量对等或者是倍数,不然可能会有消费倾斜**
- 每个consumer通过**balanced**维护processQueueTable
- processQueueTable为当前consumer的消费queue
- processQueueTable中有
- ProcessQueue :维护消费进度,从broker中拉取回来的消息缓冲
- MessageQueue : 用来定位查找queue
负载均衡算法
平均分配策略(默认)(AllocateMessageQueueAveragely)
环形分配策略(AllocateMessageQueueAveragelyByCircle)
手动配置分配策略(AllocateMessageQueueByConfig)
机房分配策略(AllocateMessageQueueByMachineRoom)
一致性哈希分配策略(AllocateMessageQueueConsistentHash)
靠近机房策略(AllocateMachineRoomNearby)
MQ与DB一致性原理(两方事务):
- 一个topic分布在多个broker上,一个broker可以配置多个topic,它们是多对多的关系。
- 如果某个topic消息量很大,应该给它多配置几个队列,并且尽量多分布在不同broker上,减轻某个broker的压力。
- topic消息量都比较均匀的情况下,如果某个broker上的队列越多,则该broker压力越大。
RocketMQ如何做负载均衡
Topic在Broker集群中分布式存储
Producer端:轮询
Consumer端:平均分配策略,一个队列最多被一个消费组的一个Consumer消费,一个Consumer可以消费多个队列
重复消费的原因
- Consumer消费完,宕机,未返回ACK
- Consumer消费完,返回ACK,网络断开,Broker未收到
- 主Broker更新ACK,副Broker未复制,主Broker宕机
消息被重复消费,如何保证(也就是说,在某个consumser已经消费过了,由于网络波动或者其他原因导致的消息重复被消费)?
broker与nameserver关系
- 连接
- 单个broker和所有nameserver保持长连接
- 心跳
- 心跳间隔:每隔30秒(此时间无法更改)向所有nameserver发送心跳,心跳包含了自身的topic配置信息。
- 心跳超时:nameserver每隔10秒钟(此时间无法更改),扫描所有还存活的broker连接,若某个连接2分钟内(当前时间与最后更新时间差值超过2分钟,此时间无法更改)没有发送心跳数据,则断开连接。
- 断开
- 时机:broker挂掉;心跳超时导致nameserver主动关闭连接
- 动作:一旦连接断开,nameserver会立即感知,更新topc与队列的对应关系,但不会通知生产者和消费者
- 一旦某个broker master宕机,生产者和消费者多久才能发现?受限于rocketmq的网络连接机制,默认情况下,最多需要30秒,但这个时间可由应用设定参数来缩短时间。这个时间段内,发往该broker的消息都是失败的,而且该broker的消息无法消费,因为此时消费者不知道该broker已经挂掉。
- 消费者得到master宕机通知后,转向slave消费(重定向,对于2次开发者透明),但是slave不能保证master的消息100%都同步过来了,因此会有少量的消息丢失。但是消息最终不会丢的,一旦master恢复,未同步过去的消息会被消费掉。
- 所有发往broker的消息,有同步刷盘和异步刷盘机制,总的来说,可靠性非常高
- 同步刷盘时,消息写入物理文件才会返回成功,因此非常可靠
- 异步刷盘时,只有机器宕机,才会产生消息丢失,broker挂掉可能会发生,但是机器宕机崩溃是很少发生的,除非突然断电
- 扫描间隔
- 默认10秒,由broker配置参数cleanResourceInterval决定
- 空间阈值
- 物理文件不能无限制的一直存储在磁盘,当磁盘空间达到阈值时,不再接受消息,broker打印出日志,消息发送失败,阈值为固定值85%
- 清理时机
- 默认每天凌晨4点,由broker配置参数deleteWhen决定;或者磁盘空间达到阈值
- 文件保留时长
- 默认72小时,由broker配置参数fileReservedTime决定
- 连接
- 单个消费者和一台nameserver保持长连接,定时查询topic配置信息,如果该nameserver挂掉,消费者会自动连接下一个nameserver,直到有可用连接为止,并能自动重连。
- 心跳
- 与nameserver没有心跳
- 轮询时间
- 默认情况下,消费者每隔30秒从nameserver获取所有topic的最新队列情况,这意味着某个broker如果宕机,客户端最多要30秒才能感知。该时间由DefaultMQPushConsumer的pollNameServerInteval参数决定,可手动配置。
- 连接
- 单个消费者和该消费者关联的所有broker保持长连接。
- 心跳
- 默认情况下,消费者每隔30秒向所有broker发送心跳,该时间由DefaultMQPushConsumer的heartbeatBrokerInterval参数决定,可手动配置。broker每隔10秒钟(此时间无法更改),扫描所有还存活的连接,若某个连接2分钟内(当前时间与最后更新时间差值超过2分钟,此时间无法更改)没有发送心跳数据,则关闭连接,并向该消费者分组的所有消费者发出通知,分组内消费者重新分配队列继续消费
- 断开
- 时机:消费者挂掉;心跳超时导致broker主动关闭连接
- 动作:一旦连接断开,broker会立即感知到,并向该消费者分组的所有消费者发出通知,分组内消费者重新分配队列继续消费
- 本地队列
- 消费者不间断的从broker拉取消息,消息拉取到本地队列,然后本地消费线程消费本地消息队列,只是一个异步过程,拉取线程不会等待本地消费线程,这种模式实时性非常高(本地消息队列达到解耦的效果,响应时间减少)。对消费者对本地队列有一个保护,因此本地消息队列不能无限大,否则可能会占用大量内存,本地队列大小由DefaultMQPushConsumer的pullThresholdForQueue属性控制,默认1000,可手动设置。
- 轮询间隔
- 消息拉取线程每隔多久拉取一次?间隔时间由DefaultMQPushConsumer的pullInterval属性控制,默认为0,可手动设置。
- 消息消费数量
- 监听器每次接受本地队列的消息是多少条?这个参数由DefaultMQPushConsumer的consumeMessageBatchMaxSize属性控制,默认为1,可手动设置。
消息堆积如何处理
1、增加Consumer,增加MessageQueue,增加Consumer线程数
2、新建一个Topic,先消费将消息搬运到另外一个Topic,后用新Consumer消费处理
rocketmq怎么保证队列完全顺序消费?
RocketMQ可以严格的保证消息有序。但这个顺序,不是全局顺序,只是分区(queue)顺序。要全局顺序只能一个分区。
也就是说:同一个topic,在broker中默认会存在多个queue,因此,不论是producer还是customer,都不能保证生产或者消费的顺序性,即消费端消费的时候,是会分配到多个queue的,多个queue是同时拉取提交消费。生产者同样。
在同一条queue里面,RocketMQ的确是能保证FIFO的。那么要做到顺序消息,必须把消息确保投递到同一条queue。