消息队列:Kafka基本概念和逻辑

Kafka基本概念和逻辑,以及设计目的,如Topic、Partition、Offset等;

Kafka基本概念

Broker

Kafka集群的一个进程,在集群内由唯一id进行标识;当Broker接到消息,将其存储在磁盘,同时根据配置将消息复制到其他Broker,保证消息的可靠性;消费时根据请求,从磁盘读取相应的消息返回给消费者;

  • 独立接收消息请求;
  • 与其他Broker通讯协同;

Topic

Topic是一个逻辑概念,用于对消息进行分类管理,通常一个topic对应一类业务消息;简单来说:生产者将消息发送到特定的 Topic,消费者则从感兴趣的 Topic 中获取消息;

  • 一个Topic对应多个partition;

Partition

Partition是Topic下的物理分区。一个 Topic 可以包含多个 Partition每个 Partition 都是一个有序的、不可变的消息序列消息在 Partition 中按照顺序被分配一个唯一的偏移量offset

Partition核心目的:实现数据的分布式存储和并行消费;

Partition和消费者的关系

  • 同一个Partition只能被同一个消费者组的一个消费者消费;(否则出现重复消费,offset是以消费者组来记录的)
  • 同一个消费者,可以消费同一个Topic下的多个Parition;(每个Partition的消息不会重复,可以正常消费多个Parition)
  • 最佳配比:
    消费者数量 == partition分区数

Partition的高可用

  • 每个Partition可以根据配置存储多个副本;通常副本数量为
    Broker节点数 - 1
    ,即:Broker节点数 = Leader + Follower
  • Leader:负责所有的读写任务(生产消费任务);
  • Follower:仅负责备份消息;当Leader故障,通过选举进行故障转移;(选举的过程设计ISR机制)

ISR(In - Sync Replicas,同步副本集):对于分区的Leader来说,可靠的副本集合;

  • 可靠的副本:消息备份落后时间在一定范围内(
    replica.lag.time.max.ms
    )以及消息数量落后在一定范围内(
    replica.lag.max.messages
    ),被认为是可靠的、可以用于故障转移和数据恢复的副本;
  • 消息的确认机制、故障转移机制都只会考虑ISR,而不是全部的副本;
  • ISR是动态维护的,副本可能被踢出ISR,也可以重新加入ISR;

Partition和消费者的分配策略

  • Range:按照分区的范围进行分配,将主题的分区按照一定的规则划分成多个范围,然后将每个范围分配给一个消费者;
  • RoundRobin:将主题的分区依次轮流分配给消费者组中的各个消费者;
  • Sticky:尽可能地将分区分配给相同的消费者,以减少分区的重新分配;
  • 如果消费者数量大于分区数,则会存在空闲的消费者;

Partition和消费者数量关系一个消费者,可以消费多个Partition;一个Partition只能被同组一个消费者消费,可以被不同组多个消费者消费; 同一个topic下的Partition数量(P)与同一个消费者组下的消费者数量(C)的关系:

  • 当C < P,就存在某消费者消费多个Partition;
  • 当C = P,效率最高,最大并行度;
  • 当C > P,多余的消费者,不会消费这个Topic数据;

Offset

生产端offset:当生产者向 Kafka 主题的某个分区写入消息时,每条消息都会被分配一个唯一的 offset。这个 offset 是一个单调递增的整数,

由分区自身维护

消费端offset:offset消费进度是以消费者组为单位,每个消费者组在每个Partition中都有自己独立的offset,存储在kafka的内部主题

__consumer_offsets
中;当消费完成后,需要确认offset来记录消费位置;参数:
enable.auto.commit

  • 自动提交offset:消费者可以将
    enable.auto.commit
    参数设置为
    true
    ,从而开启自动提交 offset 功能。在这种模式下,消费者会按照
    auto.commit.interval.ms
    参数指定的时间间隔,定期将当前消费到的 offset 提交到
    __consumer_offsets
    主题
  • 手动提交offset:消费者也能将
    enable.auto.commit
    参数设置为
    false
    ,然后手动提交 offset。手动提交能让消费者更精确地控制 offset 的提交时机,避免因自动提交导致的消息重复消费或丢失问题

offset重置策略:当消费者组首次消费某个主题,或者指定的 offset 不存在时,就需要采用 offset 重置策略;参数:

auto.offset.reset

  • earliest:从分区的起始位置开始消费消息。
  • latest:从分区的最新位置开始消费消息。

Producer

通常指Kafka的生产客户端:负责将消息发送到Kafka服务端,投递到对应的Topic甚至Parition中(如果指定了Partition);

Consumer

消费者组:一组相关的消费者,它们共同消费一个或多个 Topic 中的消息;

消费者组的设计目的:在一个 Consumer Group 中,每个消费者负责消费 Topic 中不同 Partition 的消息,从而实现并行消费和负载均衡

消费者:指kafka的消费客户端,根据订阅的topic从Kafka服务端的某个Partition按顺序拉取消息;

Kafka的存储

1. 存储结构

Kafka的Topic分多个Partition来提高并行度,每个Partition又由多个Segment构成,实现消息存储; 每个Segment段分为:

.index
 + 
.log
 + 
.timeindex

  • .log
    文件:采用追加写的方式,追加到1G大小,生产一个新的Segment;
  • .log
    文件:记录每一条消息的元数据,包括在此文件内的偏移地址,创建时间,消息存储的物理地址;
  • .index
    文件(稀疏索引):log日志每追加4kb,在index中记录追加的消息起始偏移量、起始position;
  • .index
    文件:用于通过offset进行索引消息;会以最小offset命名;
  • .timeindex
    文件:用于通过时间戳进行索引消息;

2. 消息寻址

寻址主要通过下面几个参数:

  • .index
     [消息偏移量,position指针],position指向.log的position
  • .log
     [消息offset,position指针]

假设查找offset:177854消息寻址过程:(二分搜索)

  1. 搜索到17784在[17780-18253]之间,就锁定了以17780为起始的segment(.index文件)
  2. 根据
    .index
    记录,找到offset的偏移位置区间,17783在17780+3~6的偏移位置之间,因此锁定指针153
  3. .log
    文件中从153开始向下顺序查找,找到offset 17784消息
  4. 从磁盘读取并发送此条消息(零拷贝)

Kafka的副本机制

Kafka的高性能

1. Parition的并行消费

当消费者数量对应Partition数量,可以达到最大消费速率;

可以增加Partition数量,横向扩展并行度;

2. 生产消费批处理

批量发送 和 批量拉取极大提升吞吐量;

3. 零拷贝技术

减少数据拷贝次数,线程上下文切换次数;

  • 索引文件:mmap + write;通常是消费端指定offset读取时用到;
  • 消息文件:sendfile;通过索引,找到消息后,通过sendfile发送消息;

4. 顺序写/顺序读/磁盘预读

  • 顺序写只需要数据写入时间,减少寻道、磁头旋转时间
  • 大量利用page cache,写数据不需要真正写入磁盘
  • 读操作可以利用磁盘预读,连续读取多个页面,减少磁盘IO;

5. 高效存储:稀疏索引 + 二分搜索

二分搜索 + 稀疏索引 实现高效查询操作(通过时间戳和offset)

  • 通过时间戳和offset 进行二分搜索;
  • 稀疏索引:多条消息才会生成一个索引;

6. 消息压缩

消耗一定的CPU,提高网络传输效率,提高吞吐量;

  • 生产端可用通过配置:
    compression.type = gzip
    对消息进行单个或批量压缩;减少数据大小;
  • 生产端同样配置有压缩算法;当与生产端压缩算法相同,则不需要解压缩,并且能够用到零拷贝;
    • 当压缩算法不一致,就需要对数据进行解压,再次压缩,势必需要将数据拷贝到用户空间进行处理;
    • 当压缩算法一致,不需要对消息进行额外处理,
  • 消费端直接拉取压缩的数据,在客户端进行解压缩;