消息队列kafka | 青训营笔记

63 阅读5分钟

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

消息队列kafka

使用场景

img.png

Metrics数据:在程序运行期间采集的程序状态数据

如何使用kafka

  1. 创建集群
  2. 新建Topic
  3. 编写生产者逻辑
  4. 编写消费者逻辑

img_1.png

基本概念

img_2.png

  1. Partition:Topic内部的分区,Partition之间可并发处理
  2. 对同一个Topic,不同消费组之间互不干涉

Offset

img_3.png

Replica (副本)

img_4.png

In-Sync Replicas(ISR):Leader与Follower间差距过大时(差距可设置),Follower将被踢出同步ISR

  • 以前是按offset差距判断,现在是按时间差距判断
  • 若Leader所在副本的机器宕机,则从ISR中选择一个Follower作为新的Leader

数据复制

img_5.png

每个Broker为一个节点,所有的Broker节点构成集群

Controller:负责对集群中的副本及Broker进行分配

由Controller计算出各个topic及分片的Leader所属Broker

Kafka架构

img_6.png

一条消息的自述

img_7.png 思考:若每发送一条消息,等其成功后再发一条会有什么问题

img_8.png

Producer-批量发送

img_9.png

思考:如果消息量很大,网络带宽不够用,如何解决

数据压缩

img_10.png

Broker-数据的存储

img_11.png

消息文件结构

副本最终会以日志形式写入磁盘,而消息有过期机制

  • 每个日志会切分为不同的日志段LogSegment
  • 每个日志段有4个部分
    • .log日志部分
      • 存储真实的日志数据
    • .index 消息偏移量索引文件
      • offset 与数据在真实数据文件具体位置的映射
    • .timeindex 时间戳索引文件
      • 通过时间戳表示索引
    • 其他文件

img_12.png

Broker-磁盘结构

img_13.png

顺序写可减少寻道带来的时间成本

kafka采用顺序写以提高写入效率

img_14.png

采用末尾添加的方式添加消息

Broker-如何找到消息

img_15.png

偏移量索引文件

目标:寻找offset=28

img_16.png

broken内采用稀疏索引方式进行索引的构建

  1. 二分找到小于目标offset的最大文件(例子中找到的是26)
  2. 取出相应的Batch,通过遍历的方式找到offset为28的数据

img_17.png

时间戳索引文件

二分找到小于目标时间戳的最大索引位置,再通过寻找offset的方式找到最终数据

img_18.png

数据拷贝

  1. 传统数据拷贝

    磁盘->Read Buffer->Socket buffer->NIC Buffer-> 消费者进程

img_19.png 2. 零拷贝(读)

在内核态直接将数据拷贝给网卡

img_20.png

  1. 零拷贝(写)

    在用户态进行写之后,直接拷贝到磁盘而不经过内核态(利用mmap)

Consumer-消息的接收端

img_21.png

  1. 手动分配

    • 生产者启动时,在代码里配置好不同Consumer拉取哪些Partition
    • 缺陷
      • 当某个Consumer宕机后,其对应的Partition数据流将断掉(不能自动容灾)
      • 当有新的Consumer(无新的Partition)时,必须停掉部分现有Consumer与Partition的连接,为新Consumer配置Partition(带来机器的启动与停止,引起数据中断)
    • 优点:快(提前配置好)

img_23.png 2. 自动分配

  • 在Broker集群中,选择其中一个Broker当Coordinator(协调者),帮助该group内的consumer自动分配分片(partition)(分配过程称为rebalance)
  • 当某个consumer宕机时,会自动将其原持有的分片分配给其他consumer
  • 当新增consumer时,重新计算每个consumer持有的分片

img_22.png

Rebalance

  1. Consumer向负载较低的Broker发送FindCoordinatorRequest请求,以获取Broker中Coordinator的信息
  2. Consumer向Coordinator发送JoinGroupRequest表示想要加入整个group
  3. Coordinator收到请求后,从Consumer中选取一个Leader,由Leader计算分配策略(虽然kafka有默认分配策略,但有时业务会有特殊分配需求,故提供方法供业务自己实现分配方式)
  4. 通常选取Group中的第一个Consumer作为Leader,向所有Consumer发送JoinGroupResponse,对Leader额外附加 id.leader=true
  5. Consumer向Coordinator发送SyncGroupRequest,Leader发送的请求还会额外附加上分配方案。随后Coordinator向每个Consumer发送该分配方案
  6. 每个Consumer向Coordinator定期发送心跳(HeartBeatRequest)。某Consumer未及时发送心跳,将被认为出现故障,触发Rebalance重新进行分配

img_24.png

总结:Kafka用于提高吞吐或稳定性的功能

  1. Producer:批量发送、数据压缩
  2. Broker:顺序写、消息索引、零拷贝
  3. Consumer:Rebalance

img_25.png

kafka缺点

数据复制问题

img_26.png

重启、替换、扩容与缩容等都会因数据复制带来额外的时间成本

  • 重启操作
    • 当旧Leader重启时,将会选用新的Leader
    • 待重启完毕,因在此期间仍然不断要接收新的数据,旧Leader需要不断追赶与新Leader间的数据差异
    • 待数据同步完成后,旧Leader将重新获得Leader身份(Leader回切)
    • 若没有回切操作,待陆续重启后,所有的Leader都将集中于最后的Broker上,带来数据压力,引起负载均衡问题
    • 当Broker数量多时,陆续重启将花费很长的时间
    • 并发重启可能导致该分片不可用

img_27.png

负载不均衡

当某个Partition数据较多时,该Partition将被迁至更合适的Broker中以达到负载均衡

(迁走前)

img_28.png

(迁走后)

img_29.png

  1. 对Partition进行迁移是为了通过负载均衡降低IO负担。
  2. 而迁走后又会引入进行数据复制问题,加重IO负担,带来死循环(引入的IO问题)
  3. 故需要权衡两者利弊,设计复杂的负载均衡问题

Kafka问题总结

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