走进消息队列(上) | 青训营笔记

132 阅读5分钟

这是我参与「第五营」笔记创作活动的的第16天

走进消息队列(上)

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

消息队列的前世

发展历程

image.png

业界消息队列对比

Kafka: 分布式的、分区的、多副本的日志提交服务,在高吞吐场景下发挥较为出色。

RocketMQ: 低延迟、强一致、高性能、高可靠、万亿级容量和灵活的可扩展性,在一些实时场景中运用较广。

Pulsar: 是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体.采用存算分离的架构设计。

BMQ: 和Pulsar架构类似,存算分离,初期定位是承接高吞吐的离线业务场景,逐步替换掉对应的Kafka集群。

Kafka

使用场景

  • 业务日志信息
  • 搜索服务、直播服务、订单服务和支付服务的Metrics数据
  • 用户行为数据——搜索、点赞、评论、收藏、

如何使用

  1. 首先需要创建一个Kafka集群
  2. 需要在这个集群中创建一个Topic,并且设置好分片数量。
  3. 编写生产者逻辑,初始化一个生产者,调用Send方法,将你的Hello World发送出去。
  4. 编写消费者逻辑,初始化一个消费者,调用Poll方法,你将收到你刚刚发送的Hello World。

基本概念

Topic: 逻辑队列,不同 Topic 可以建立不同的 Topic

Cluster: 物理集群,每个集群中可以建立多个不同的 Topic

Producer: 生产者,负责将业务消息发送到 Topic 中

Consumer: 消费者,负责消费 Topic 中的消息

ConsumerGroup: 消费者组,不同组 Consumer 消费进度互不干涉

image.png

Offset : 对于每一个Partition来说,每一条消息都有一个唯一的Offset,消息在 partition 内的相对位置信息,可以理解为唯一ID,在 partition 内部严格递增。

Replca: 分片的副本,分布在不同的机器上,可用来容灾,Leader对外服务,Follower异步去拉取Leader的数据进行同步,如果Leader掉了,可以将Follower提升Leader再外进行服务。

ISR: 意思是同步中的副本,对于Follower来说,始终和Leader是有一定差距的,但当这差距比较小的时候,我们就可以将这Follower副本加入ISR中,ISR中的副本是不允许提升成Leader的。

image.png

数据复制

image.png

架构

image.png

Producer

批量发送:批量发送可以减少IO次数,从而加强发送能力。

数据压缩:如果消息量很大,网络带宽不够用,通过压缩,减少消息大小,目前支持Snappy、 Gzip、LZ4、ZSTD压缩算法。

Broker

消息文件结构:在每一个Broker中,都分布着不同Topic的不同分片。

image.png

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

image.png

image.png

如何找到消息

偏移量索引文件:通过二分找到小于目标offset最大索引位置,再遍历找到目标offset。

image.png

时间戳索引文件:和offset相比只是多加了以及索引,也就是通过二分找到时间戳对应的offset,再重复之前的步骤找到相应的文件数据。

image.png

传统数据拷贝

image.png

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

image.png

Consumer

如何解决 Partition 在 Consumer Group 中的分配问题?

对于一个Consumer Group来说,多个分片可以并发的消费,这样可以大大提高消费的效率,但需要解决的问题是, Consumer和Partition的分配问题,该由哪一个Consumer来消费的问题。对于这个问题,我们一般有两种解决方法,手动分配和自动分配。

Low Level——手动分配

image.png

  • 优点:启动比较快,因为对于每一个Consumer来说,启动的时候就已经知道了自己应该去消费哪个消费式,就好比图中的Consumer Group1来说,Consumer1去消费Partition1,2,3;Consumer2去消费Partition4,5,6;Consumer3去消费Partition7,8。这些Consumer在启动的时候就已经知道分配方案了。
  • 缺点:如果我们的Consumer3挂掉了,我们的7,8分片是不是就停止消费了。又或者,如里我们新增了一台Consumer4,那是不是又需要停掉整个集群,重新修改配置再上一线,保Consumer4也可以消费数据,其实上面两个问题,有时对于线上业务来说是致命的。

High Level——自动分配

image.png

  • Kafka也提供了自动分配的方式,这里也叫做 High Level的消费方式,简单来说,就是在我们的Broker集群中,对于不同的Consumer Group来讲,都会选取一台Broker当做Coordinator,而Coordinator的作用就是帮助Consumer Group进行分片的分配,也叫做分片的Rebalance,使用这种方式,如果Consumer Group中有发生宕机,或者有新的Consumer加入,整个Partition和Consumer都会重新进行分配来达到一个稳定的消费状态。

问题总结

  1. 运维成本高。因为有数据复制的问题,所以Kafka运维的时间成本和人力成本都不低。
  2. 对于负载不均衡的场景,我们需要有一个较为复杂的解决方案进行数据迁移,从而来权衡IO升高的问题。
  3. Kafka没有自己的缓存,在进行数据读取的时候,只有Page Cache可以用,所以不是很灵活。
  4. Kafka的Controller、Coordinator和Broker在同一进程中,Broker因为承载大量IO的原因,会导致Controller和Coordinator性能下降,如果到了一定程度,可能会影响整个集群的可用性。