消息队列原理与实战| 青训营笔记

106 阅读5分钟

消息队列MQ

这是我参与「第五届青训营」伴学笔记创作活动的第 13 天

消息队列(MQ):指保存消息的一个容器,本质是一个队列。但是该队列需要支持高吞吐、高并发、高可用

发展

  • 业界MQ对比

image.png

Kafka

使用步骤

image.png

基本概念

  • Topic:逻辑队列,不同Topic可以建立不同的Topic
  • Cluster:物理集群,每个集群中可以建立多个不同Topic
  • Producer:生产者,负责将业务消息发送到Topic中
  • Consumer:消费者,负责消费Topic中的消息
  • ConsumerGroup:消费者组,不同组Consumer消费进度互不干涉
  • Partition:通常topic会有多个分片,不同分片之间消息是可以并发来处理的,这样提高单个Topic的吞吐

image.png

  • Offset:消息在 partition内的相对位置信息,可以理解为唯一ID,在 partition内部严格递增。

image.png

  • 每个分片有多个 Replica, Leader Replica将会从 ISR中选出。
  • Replica:分片的副本,分布在不同的机器上,可用来容灾,Leader对外服务,follwer异步去拉取leader的数据进行一个同步,如果leader挂掉了,可以将follower提升成leader再对外进行服务
  • lSR:意思是同步中的副本,对于Follower来说,始终和leader是有一定差距的,但当这个差距比较小的时候,我们就可以将这个follower副本加入到ISR中,不在ISR中的副本是不被允许提升为Leader

数据复制

image.png

架构

image.png

一条信息的存储过程

image.png

数据压缩

image.png

消息文件结构

image.png

磁盘结构

移动磁头找到对应磁道,磁盘转动,找到对应扇区,最后写入。寻道成本比较高,因此顺序写可以减少寻道所带来的时间成本。

查找消息

image.png 二分查找

image.png

读取数据

零拷贝

image.png

  • Consumer从Broker中读取数据,通过sendfile的方式,将磁盘读到os内核缓冲区后,直接转到socket buffer进行网络发送 Producer生产的数据持久化到broker,采用mmap文件映射,实现顺序的快速写入

消息接收端的消费

  • 手动分配

    • 如果我们的Consumer3挂掉了,我们的7,8分片是不是就停止消费了。又或者,如果我们新增了一台Consumer4,那是不是又需要停掉整个集群,重新修改配置再上线,保证Consumer4也可以消费数据,其实上面两个问题,有时候对于线上业务来说是致命的。 image.png
  • 自动分配

    • 在我们的Broker集群中,对于不同的Consumer Group来讲,都会选取一台Broker当做Coordinator,而Coordinator的作用就是帮助Consumer Group进行分片的分配,也叫做分片的rebalance,使用这种方式,如果ConsumerGroup中有发生宕机,或者有新的Consumer加入,整个partition和Consumer都会重新进行分配来达到一个稳定的消费状态

image.png

Rebalance

image.png

小总结

image.png

数据复制问题

image.png 通过前面的介绍我们可以知道,对于Kafka来说,每一个Broker上都有不同topic分区的不同副本,而每一个副本,会将其数据存储到该Kafka节点上面,对于不同的节点之间,通过副本直接的数据复制,来保证数据的最终一致性,与集群的高可用。

重启操作

image.png 回切的必要: 在众多 leader 的转移过程中,就会产生 leader 不均衡现象,可能一小部分 broker 上有大量的 leader,影响了整个集群的性能,所以就需要把 leader 调整会最初的 broker 上,这就需要 preferred leader 选举。

替换、扩容、所容

本质还是数据复制问题

负载不均衡

image.png

image.png 但我们的数据复制又会引起Broker1的IO升高,所以问题就变成了,我为了去解决IO升高,但解决问题的过程又会带来更高的IO,所以就需要权衡IO设计出一个极其复杂的负载均衡策略

BMQ

  • 兼容Kafka协议,存算分离,云原生消息队列

架构图

image.png

image.png

文件结构

image.png 对于Kafka分片数据的写入,是通过先在Leader上面写好文件,然后同步到Follower上,所以对于同一个副本的所有Segment都在同一台机器上面。就会存在之前我们所说到的单分片过大导致负载不均衡的问题,但在BMQ集群中,因为对于单个副本来讲,是随机分配到不同的节点上面的,因此不会存在Kafka的负载不均问题

Partition状态机

image.png

写文件流程

image.png

  • Thread的具体逻辑,首先会将Buffer中的数据取出来,调用底层写入逻辑,在一定的时间周期上去flush,flush完成后开始建立Index,也就是offset和timestamp对于消息具体位置的映射关系 Index建立好以后,会save一次checkpoint,也就表示,checkpoint后的数据是可以被消费的

  • 我们想一下,如果没有checkpoint的情况下会发生什么问题,如果flush完成之后宕机,index还没有建立,这个数据是不应该被消费的

Proxy

image.png

多机房部署

  • 防止机房级故障带来的影响

image.png

泳道消息

image.png

image.png

Databus

image.png

Mirror

image.png

Index

如果希望通过写入的Logld、Userld 或者其他的业务字段进行消息的查询,应该怎么做?

image.png

Parquet

Apache Parquet是Hadoop生态圈中一种新型列式存储格式,它可以兼容Hadoop生态圈中大多数计算框架(Hadoop、Spark等),被多种查询引擎支持(Hive、lmpala、Drill等)。

image.png

RocketMQ

image.png

image.png

存储模型

image.png

事务消息

  • 相当于二阶段提交 image.png

延迟消息

image.png

消费重试和死信队列

image.png