消息队列 | 青训营笔记

50 阅读9分钟

消息队列

定义

针对四个问题:

  • 系统崩溃

    解决方案:解耦

    graph LR;
    User((user))-->a[搜索商品]-->b[搜索行为记录]-->c[点击商品]-->d[点击行为记录]
    b-->e[消息队列]
    d-->e-->f[存储服务]
    
  • 服务处理能力有限

    解决方案:削峰(只能处理10个请求就拿10个请求)

byteDance7-1.png

  • 链路耗时长尾

byteDance7-2.png

  • 日志如何处理

    解决方案:

byteDance7-3.png

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

前生今生

byteDance7-4.png

byteDance7-5.png

Kafka

byteDance7-6.png

Metrics数据:在程序运行当中对程序进行一些状态的采集 (QPS、查询耗时、写入耗时等)

如何使用Kafka

graph TD;
a[创建集群]-->b[新增Topic]-->c[编写生产者逻辑]-->d[编写消费者逻辑]

基本概念

byteDance7-7.png

Topic:逻辑队列,不同Topic可以建立不同的Topic(每一个不同的场景就是一个topic)

Partition:Topic中的分区的概念,不同的分区可以并发处理消息

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

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

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

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

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

byteDance7-8.png

每个分片有多个Replica,Leader Replica将会从ISR中选出

byteDance7-9.png

Leader:主副本,从Replica1-Leader上进行生产和消费

Follwer:副本,与主副本保持一定的同步(offset/时间),如果差距太大将会被踢出ISR(In-Sync-Relicas)如Replica3。

如果Leader中发生了宕机,那么就可以从ISR的follower中继续为生产者和消费者进行服务。保证高可用。

数据复制

byteDance7-10.png

每个broker代表kafka集群中的一个节点,集群中有两个topic,topic1有两个partition,topic2有1个partition,每一个partition是3个replica的状态。

其中第二个broker扮演了controller的角色,controller是整个集群中的核心,负责分配broker、replica、partition。

每一个consumer group都是独立的,意味着每一个group都需要获得所需要的topic的全量数据

架构图

byteDance7-11.png

批量发送

通过batch操作

byteDance7-12.png

如果遇到消息量很大,网络带宽不够用,如何解决?

采用数据压缩(推荐ZSTD算法)

byteDance7-13.png

数据的存储

broker是如何将消息存储到本地磁盘中呢?

byteDance7-14.png

broker-磁盘结构

byteDance7-15.png

采取顺序写的方式进行写入,以提高写入效率

如何找到消息

consumer通过发送FetchRequest请求消息数据,Broker会将指定Offset处的消息,按照时间窗口和消息大小窗口发送给Consumer,寻找这个细节是如何做到的呢?

偏移量索引文件

每一个logsegment文件的命名方式都是以第一条消息的offset来命名的

byteDance7-16.png

通过offset进行索引

byteDance7-17.png

通过时间戳进行索引(二级索引表)

byteDance7-18.png

传统数据拷贝

byteDance7-19.png

Broker——零拷贝

传统的数据传输方式涉及多次数据拷贝操作。例如,当应用程序从磁盘读取数据时,数据首先被拷贝到内核缓冲区,然后再次被拷贝到用户空间缓冲区,最后才能被发送到网络。这些数据拷贝操作会占用CPU时间和内存带宽,对系统性能产生影响。

而零拷贝技术通过避免数据的不必要拷贝,将数据直接从源缓冲区传输到目标缓冲区,以减少数据拷贝的次数。在 Kafka 中,零拷贝技术主要应用在生产者和消费者之间的数据传输过程中。

具体来说,Kafka 利用操作系统提供的 sendfile() 系统调用或类似的机制,实现了零拷贝。在生产者端,Kafka 使用 sendfile() 将数据从磁盘直接传输到网络套接字,而无需经过额外的数据拷贝。在消费者端,Kafka 利用零拷贝技术将数据从网络套接字直接写入到内存缓冲区,避免了额外的数据拷贝操作。

通过使用零拷贝技术,Kafka 在数据传输过程中能够更高效地利用系统资源,减少了不必要的数据拷贝和上下文切换操作,提高了数据传输的吞吐量和性能,同时降低了CPU和内存的开销。这对于处理大量数据和实现低延迟的消息传递非常有益。

byteDance7-20.png

消息的接收端

byteDance7-21.png

low level(消费方式)

byteDance7-22.png

  • 不能容灾,如果consumer3挂掉了,那么topic中的partition7和partition8的数据流断掉了

  • 新建consumer时需要重新分配,启停之前正在消费的consumer

high level(消费方式)

自动分配

bytedance7-23.png

怎么达到具体的Rebalance?

  1. 消费者向集群中负载最低的broker发送findCoordinatorRequest,该broker将coordinator信息发送回consumer
  2. 消费者向coordinator发送JoinGroupRequest,形成consumer group,coordinator也会选取一台consumer作为leader
  3. consumer group中的leader向coordinator发送SyncGroupRequest并携带分配方案,其他consumer只需发送SyncGroupRequest。
  4. coordinator把分配方案发送给consumer group中的consumer
  5. consumer group中的consumer隔一段时间需要向coordinator发送心跳信号,如果一段时间某一个consumer没有心跳则被踢出group 重新进行分配partition

byteDance7-24.png

小结:

刚才总共讲了哪一些可以帮助kafka提高吞吐或者稳定性的功能?

Producer:批量发送、数据压缩

Broker:顺序写,消息索引,零拷贝

Consumer:Rebalance

kafka——数据复制问题

kafka集群升级业务时需要重启

byteDance7-25.png

重启代价过大

kafka—替换、扩容、缩容

跟重启类似,但替换和扩容都要从0开始追赶数据。

kafka— 负载均衡问题

byteDance7-26.png

kafka问题总结

  1. 运维成本高
  2. 对于负载不均衡的场景,解决方案复杂
  3. 没有自己的缓存,完全依赖Page Cache(文件系统自带)
  4. Controller 和 Coordinator 和 Broker在同一进程中,大量的IO会造成其性能下降。

BMQ

定义

BMQ为解决Kafka存在的问题应运而生。兼容Kafka协议,存算分离(可以将broker中的数据放到分布式存储系统中),云原生消息队列

Producer和Consumer基本和Kafka协议中的相同。

byteDance7-27.png

使用的分布式存储系统是HDFS

读写分离,存算分离,没有数据复制之后,性能大大提高

Kafka与BMQ操作性能对比

byteDance7-28.png

HDFS写文件流程

byteDance7-29.png

BMQ文件结构

byteDance7-30.png

BMQ(Balanced Message Queue)是一种用于解决 Kafka 负载不均衡问题的解决方案。Kafka 负载不均衡指的是 Kafka 集群中的分区(Partitions)在不同的 Broker 节点上分布不均匀,导致某些 Broker 节点负载过高,而其他节点负载较低的情况。

BMQ 通过以下方式解决 Kafka 负载不均衡问题:

  1. 动态分区分配:BMQ 采用动态的分区分配策略,能够根据当前 Broker 节点的负载情况,自动调整分区的分布,使得各个节点的负载更加均衡。
  2. 基于权重的分区分配:BMQ 引入了权重机制,通过为每个 Broker 节点分配不同的权重值,来影响分区的分配情况。具有较低负载的节点将被分配更多的分区,而具有较高负载的节点将被分配较少的分区,从而实现负载均衡。
  3. 动态调整机制:BMQ 在运行时会根据实时的负载情况动态调整分区的分配。当某个节点的负载过高或过低时,BMQ 将根据预先设定的策略进行重新分配,以使得负载更加均衡。
  4. 容错机制:BMQ 考虑了 Kafka 集群的容错性,在进行分区分配时会考虑到故障节点的情况,避免将分区分配给故障节点或无法正常提供服务的节点。

通过上述机制,BMQ 能够有效地解决 Kafka 负载不均衡的问题,提高集群的整体性能和稳定性。它能够动态地调整分区的分配,使得各个节点的负载均衡,并考虑到节点故障情况,保证了系统的可用性和可靠性。

Broker—Partition状态机

byteDance7-31.png

对于partition来讲的话,最终是写在HDFS中的。不同broker中的partition(可能不止一个)向controller争夺写入HDFS的权利。写入的过程可能存在问题,首先Recover保证云数据和真实数据保持一致。

Broker——写文件流程

byteDance7-32.png

Broker—写文件 failover

如果DataNode节点挂了或者是其他原因导致我们写文件失败,应该如何处理?

换一个可用的DataNode,创建一个新的segment

Proxy(读文件)

byteDance7-33.png

设置时间窗口,降低请求压力

多机房部署

byteDance7-34.png

Proxy是全量的,每一个Broker保存一部分的partition。如果A机房宕机了,那么其相应的broker中的partition会rebalance到其他两个broker中去。

BMQ高级特性

byteDance7-35.png

泳道消息:

——产生原因

byteDance7-36.png

BOE测试:多人同时测试,需要等待上一个人测试完成

每多一个测试人员,都需要重新搭建一个相同配置的Topic,造成人力和资源的浪费。

PPE验证:

byteDance7-37.png

泳道应运而生

byteDance7-38.png

Databus

直接使用原生SDK会有什么问题?

  1. 客户端配置较为复杂
  2. 不支持动态配置,更改配置需要停掉服务
  3. 对于latency不是很敏感的业务,batch效果不佳

byteDance7-39.png

Mirror

byteDance7-40.png

byteDance7-41.png

Index

graph LR;
Producer-->BMQ-->Consumer

Query By:

  1. Offset
  2. Timestamp

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

直接在BMQ中将数据结构化,配置索引DDL,异步构建索引后,通过Index Query服务读出数据

byteDance7-42.png

Parquet

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

byteDance7-43.png

RocketMQ

使用场景:针对电商业务线,其业务设计广泛,如注册、订单、库存、物流等;同时,也会涉及许多业务峰值时刻,如秒杀活动、周年庆、定期特惠等(低延时)

基本概念

byteDance7-44.png

byteDance7-45.png

架构

byteDance7-46.png

存储模型

密集索引

byteDance7-47.png

高级特性

byteDance7-48.png

事务消息

byteDance7-49.png

事务的两阶段提交

延迟发送

graph TD;
员工((员工))-->提前编辑菜单-->消息队列-->定时发送-->接收菜单

延迟消息原理:

byteDance7-50.png

处理失败:

byteDance7-51.png