MQ篇总结

1 RocketMQ 整体架构设计 

   

2 Broker

        Broker是RocketMQ的核心模块,负责接收并存储消息,同时提供Push/Pull接口来将消息发送给Consumer。

        Consumer可选择从Master或者Slave读取数据。多个主/从组成Broker集群,集群内的Master节点之间不做数据交互。

        Broker同时提供消息查询的功能,可以通过MessageID和MessageKey来查询消息。Borker会将自己的Topic配置信息实时同步到NameServer。

       CommitLog

        RocketMQ 的所有主题的消息都存在 CommitLog 中,单个 CommitLog 默认 1G,并且文件名以起始偏移量命名,固定 20 位,不足则前面补 0,比如 00000000000000000000 代表了第一个文件,第二个文件名就是 00000000001073741824,表明起始偏移量为 1073741824,以这样的方式命名用偏移量就能找到对应的文件。所有消息都是顺序写入的,超过文件大小则开启下一个文件。

        ConsumeQueue

        ConsumeQueue 消息消费队列,可以认为是 CommitLog 中消息的索引,因为 CommitLog 是糅合了所有主题的消息,所以通过索引才能更加高效的查找消息。

        ConsumeQueue 存储的条目是固定大小,只会存储 8 字节的 commitlog 物理偏移量,4 字节的消息长度和 8 字节 Tag 的哈希值,固定 20 字节。

        在实际存储中,ConsumeQueue 对应的是一个Topic 下的某个 Queue,每个文件约 5.72M,由 30w 条数据组成。

        消费者是先从 ConsumeQueue 来得到消息真实的物理地址,然后再去 CommitLog 获取消息。

        IndexFile

        IndexFile 就是索引文件,是额外提供查找消息的手段,不影响主流程。

        通过 Key 或者时间区间来查询对应的消息,文件名以创建时间戳命名,固定的单个 IndexFile 文件大小约为400M,一个 IndexFile 存储 2000W个索引。

        消息到了先存储到 Commitlog,然后会有一个 ReputMessageService 线程接近实时地将消息转发给消息消费队列文件与索引文件,也就是说是异步生成的。

2 Queue

        Topic和Queue是1对多的关系一个Topic下可以包含多个Queue,主要用于负载均衡。发送消息时,用户只指定Topic,Producer会根据Topic的路由信息选择具体发到哪个Queue上。Consumer订阅消息时,会根据负载均衡策略决定订阅哪些Queue的消息。

        3 NameServer

        NameServer可以看作是RocketMQ的注册中心,它管理两部分数据:集群的Topic-Queue的路由配置;Broker的实时配置信息。其它模块通过Nameserv提供的接口获取最新的Topic配置和路由信息。

       4 Producer

              Producer 无非就是消息生产者,那首先它得知道消息要发往哪个 Broker ,于是每 30s 会从某台 NameServer 获取 Topic 和 Broker 的映射关系存在本地内存中,如果发现新的 Broker 就会和其建立长连接,每 30s 会发送心跳至 Broker 维护连接。

        并且会轮询当前可以发送的 Broker 来发送消息,达到负载均衡的目的,在同步发送情况下如果发送失败会默认重投两次(retryTimesWhenSendFailed = 2),并且不会选择上次失败的 broker,会向其他 broker 投递。

        5 Brock和Topic的关系

Producer 启动流程

因为 Producer 和 Consumer 都需要用 MQClientInstance,而同一个 clientId 是共用一个 MQClientInstance 的, clientId 是通过本机 IP 和 instanceName(默认值 default)拼起来的,所以多个 Producer 、Consumer 实际用的是一个MQClientInstance。

定时任务 

Producer 发消息流程

找到要发送消息的 Topic 在哪个 Broker 上,然后发送消息。

现在就知道 TBW102 是啥用的,就是接受自动创建主题的 Broker 启动会把这个默认主题登记到 NameServer,这样当 Producer 发送新 Topic 的消息时候就得知哪个 Broker 可以自动创建主题,然后发往那个 Broker。

而 Broker 接受到这个消息的时候发现没找到对应的主题,但是它接受创建新主题,这样就会创建对应的 Topic 路由信息。

2  使用MQ的目的

  1. 流量消锋,面对大量请求可以放到队列中执行

  2. 优化接口响应时间,异步处理其他逻辑

  3. 项目间解耦,避免项目间的依赖

3  消息丢失问题 

  • 生产阶段:在这个阶段,从消息在Producer创建出来,经过网络传输发送到Broker端。
  • 存储阶段: 消息在Broker端存储,如果是集群,消息会在这个阶段被复制到其他的副本上。
  • 消费阶段:Consumer从Broker上拉取消息,经过网络 传输发送在Consumer上。

        1 生产阶段丢失

         触发场景:

        发送端报错等异常情况

        解决方案:

        捕获异常重试发送        

        2 存储阶段丢失

         触发场景:

         如果生产者保证消息发送到MQ,而MQ收到消息后还在内存中,这时候宕机了又没来得及同步给从节点,就有可能导致消息丢失。

        解决方案:

        RocketMQ分为同步刷盘和异步刷盘两种方式,默认的是异步刷盘,就有可能导致消息还未刷到硬盘上就丢失了,可以通过设置为同步刷盘的方式来保证消息可靠性,这样即使MQ挂了,恢复的时候也可以从磁盘中去恢复消息。
 

        3 消费阶段丢失

        触发场景:

        消费者刚收到消息,此时服务器宕机,MQ认为消费者已经消费,不会重复发送消息,消息丢失。

        解决方案:

        RocketMQ默认是需要消费者回复ack确认,消费方不返回ack确认,重发的机制根据MQ类型的不同发送时间间隔、次数都不尽相同,如果重试超过次数之后会进入死信队列,需要手工来处理了。

4   消息积压怎么处理?

        消息积压会有两种原因, 大促期间发送端并发请求高,导致消费端消费不过来, 还有一种是消费端异常不断重试消费, 消费效率低

        1 发送端并发请求量大

        可以通过扩容消费端实例实现负载均衡平摊压力,如果短时间内没有足够的服务器资源进行扩容,没办法的办法是,将系统降级,通过关闭一些不重要的业务,减少发送方发送的数据量,最低限度让系统还能正常运转,服务一些重要业务。

        2 消费端异常(数据量很大)

        1 如果容易修复,先把问题修复,让consumer恢复正常消费

        2 如果时间来不及处理很麻烦,做转发处理,写一个临时的consumer消费方案,先把消息消费,然后再转发到一个新的topic和MQ资源,这个新的topic的机器资源单独申请,要能承载住当前积压的消息, 处理完积压数据后,修复consumer,去消费新的MQ和现有的MQ数据,新MQ消费完成后恢复原状

        3 消费端异常(暂存数据库)

        如果时间来不及处理很麻烦,做转发处理,写一个临时的consumer消费方案,把消息保存到数据库中,  修复consumer 后, 再把库里的数据发送的consumer进行消费

        4 磁盘满了,消息都写不进去了,生产端会一直抱错。

        1、捞数据,解决当前业务问题

        2、是否配置了过期清理策略,以及合理性

        3、搭新集群,将量大的topic迁移到新集群

5 RocketMQ实现原理

图片

  1. Broker在启动的时候去向所有的NameServer注册,并保持长连接,每30s发送一次心跳

  2. Producer在发送消息的时候从NameServer获取Broker服务器地址,根据负载均衡算法选择一台服务器来发送消息

  3. Conusmer消费消息的时候同样从NameServer获取Broker地址,然后主动拉取消息来消费

6 Broker是如何持久化存储消息的?

        6.1  CommitLog消息顺序写入机制

        当生产者的消息发送到一个Broker上的时候,他接收到了一条消息,他会把这个消息直接写入磁盘上的一个日志文件,叫做CommitLog,直接顺序写入这个文件,如下图。

         这个CommitLog是很多磁盘文件,每个文件限定最多1GB,Broker收到消息之后就直接追加写入这个文件的末尾,就跟上面的图里一样。如果一个CommitLog写满了1GB,就会创建一个新的CommitLog文件。

        6.2 MessageQueue在数据存储中是体现在哪里呢?

        6.3 如何让消息写入CommitLog文件近乎内存写性能的?

        Broker是基于OS操作系统的PageCache和顺序写两个机制,来提升写入CommitLog文件的性能的。

        6.3.1 顺序写

        首先Broker是以顺序的方式将消息写入CommitLog磁盘文件的,也就是每次写入就是在文件末尾追加一条数据就可以了,对文件进行顺序写的性能要比对文件随机写的性能提升很多
我们看下面图里的红圈,就是示意数据是顺序写入的

        6.3.2 PageCache异步刷盘

        数据写入CommitLog文件的时候,其实不是直接写入底层的物理磁盘文件的,而是先进入OS的PageCache内存缓存中,然后后续由OS的后台线程选一个时间,异步化的将OS PageCache内存缓冲中的数据刷入底层的磁盘文件。
        我们看下面的图,图里示意出了,数据先写入OS的PageCache缓存中,然后后续由OS自己的线程将缓存里的数据刷入磁盘中。

         所以在这样的优化之下,采用磁盘文件顺序写+OS PageCache写入+OS异步刷盘的策略,基本上可以让消息写入CommitLog的性能跟你直接写入内存里是差不多的,所以正是如此,才可以让Broker高吞吐的处理每秒大量的消息写入

      6.4 同步刷盘与异步刷盘

       6.4.1 异步刷盘

        优点: 响应速度快,吞吐量搞

        缺点: 在上述的异步刷盘模式下,生产者把消息发送给Broker,Broker将消息写入OS PageCache中,就直接返回ACK给生产者了。此时生产者就认为消息写入成功了,如果生产者认为消息写入成功了,但是实际上那条消息此时是在Broker机器上的os cache中的,如果此时Broker直接宕机,os cache中的这条数据就会丢失

        

         6.4.2  同步刷盘

        优点:  如果broker还没有来得及把数据同步刷入磁盘,然后他自己挂了,那么此时对producer来说会感知到消息发送失败了,然后你只要不停的重试发送就可以了,直到有slave broker切换成master broker重新让你可以写入消息,此时可以保证数据是不会丢的。

        缺点:  但是如果你强制每次消息写入都要直接进入磁盘中,必然导致每条消息写入性能急剧下降,导致消息写入吞吐量急剧下降

7 部署模型

 1、Product和consumer集群部署,是你开发的项目进行集群部署。

2、Broker 集群部署是为了高可用,因为Broker是真正存储Message的地方,集群部署是为了避免一台挂掉,导致整个项目KO.


 

参考地址: 《Java架构师的第一性原理》34分布式计算之分布式消息队列(AcitveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ) - 沙漏哟 - 博客园

参考地址: 51 精益求精:深入研究一下Broker是如何持久化存储消息的?_MyySophia的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值