消息队列Kafka | 青训营笔记

51 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第25天。主要讲了消息队列的特性和应用,并依次介绍了Kafka、BMQ、RocketMQ三个消息队列架构。

使用场景

一般用在离线消息处理中

  • 日志信息处理
  • Metrics数据(程序运行时状态的采集信息比如QPS、查询写入的耗时等)
  • 搜索、点赞、评论、收藏等用户行为

使用Kafka

  • 创建集群
  • 新增Topic
  • 编写生产者逻辑
  • 编写消费者逻辑

基本概念

  • Topic: 逻辑队列,不同Topic可以建立不同的Topic
  • Cluster:物理集群,每个集群中可以建立多个不同的Topic
  • Producer:生产者,负责将业务消息发送到Topic中
  • Consumer:消费者,负责消费Topic中的消息
  • ConsumerGroup:消费者组,不同组的Consumer消费进度互不干涉
  • Partition:Topic的分区,不同分区的消息可以并发处理

关系如下图:

  • Offser:消息在Partition内的相对位置信息,可以理解为唯一ID(自增)

  • Peplica:Partition的副本,有两个状态Leader和Follower,Leader用于对外进行写入和读取,Follower不断拉取Leader的数据,与之保持同步(一个机制,Follower与Leader的差距差太多会被踢掉)

  • Controller:如下图所示,Broker表示集群中的节点,集群中有一个关键的节点Controller,负责对集群中副本的分配

Kafka架构

如下图所示,一个集群中有一个Zookeeper组件,负责存储集群元信息,包括分区分配信息等。

消息从生产到消费的流程

  • Producer发送消息:为了支持高吞吐的特性,Kafka支持Producer向Broker批量发送消息以减少IO次数,加强发送能力,为了防止消息量很大以至于达到网络带宽的瓶颈的情况,Kafka会对消息数据进行压缩(Snappy、Gzip、LZ4、ZSTD等压缩算法)。
  • Broker存储数据:Broker消息文件结构如下图所示

一个Log日志会切分成多个有序的LogSegment,每个Segment包括图上的一些文件,写入文件时采用顺序写的方式以减少磁盘寻道时间提高效率。

  • Broker寻找消息:Consumer通过发送FetchRequest请求消息数据,Broker会将指定Offset处的消息按照时间窗口和消息大小窗口发送给Consumer,Broker通过二分找到小于目标offset的最大文件,然后在.index文件里通过二分找到小于目标offset的最大索引位置拿到batch后顺序遍历,时间戳索引查找同理在.timeindex文件里做相同的事。

  • Broker发送数据:Consumer从Broker中读取数据,通过sendfile的方式,将磁盘读到os内核缓冲区后,直接把数据发送到网卡而不需要经过应用空间,流程如下图所示:

  • Consumer接收消息:Kafka有对Consumer自动分配Partition的机制,如下图每个Group有一个Coordinator来管理Partiton在该Group的分配。

综上,Kafka一共提供了以下机制来提高吞吐率和稳定性

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

Kafka的问题

数据复制问题

当集群中的Leader重启后,集群进行Leader切换,旧Leader启动后会从新Leader中追赶数据,为了防止集群中的机器依次重启导致Leader频繁切换的情况发生,当数据同步完成后会进行Leader的回切。当集群成员变更(替换、扩容、缩容) 时也存在数据复制的问题。

负载不均衡

当一个Broker数据过多,要把一个Partition迁走时,迁走Partiton时会带来数据赋值问题,从而增加该Broker的IO负载。

问题总结

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