kafka 研究和实战1

来自官网的定义
Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue)消息队列。
Kafka是 一个开源的分布式事件流平台 (Event Streaming Platform)spark/flink 等级的框架。

消息队列场景

主要用来 缓存/消峰、解耦和异步通信

  1. 缓存/消峰 缓解系统不能够及时处理的大批量的并发的请求。
  2. 解耦 消息队列两端的应用没做很多的限制。
  3. 允许用户把一个消息放入队列,但并不立即处理它,然后在需要的时候再去处理它们。

前两点实际上Redis RabbitMQ 都能实现。我认为kafka独特的机制有: 分组和分区消费机制,分布式系统以及基于内存+硬盘的存储机制的统一结合。

kafka的一些核心的概念

  1. Producer:消息生产者
  2. Consumer:消息消费者
  3. Consumer Group(CG):消费者组,由多个 consumer 组成。
    消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费;消费者组之间互不影响。也就是分区和消费者一一对应。
    多个消费者组订阅相同的主题,每个消费者组的消费数据都是全量的。
    分区多,消费者组内可添加更多的消费者,实现更大量的并发消费。
  4. Broker:一台 Kafka 服务器就是一个 broker, 是独立服务的kafka 节点。
  5. Topic:可以理解为一个队列,生产者和消费者面向的都是一个 topic。
  6. Partition: Topic 下层的概念,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。使一个Topic可以分布到多个Broker上,实现负载均衡和高可用。每个分区都有Leader,Follower 分布在不同的Broker上。
  7. Replica:副本。一个 topic 的每个分区都有若干个副本,一个 Leader 和若干个Follower。
  8. Leader:主节点 发布消费的主要对象。
  9. Follower:实时从 Leader 中同步数据,保持和Leader 数据的同步。Leader 发生故障时,某个 Follower 会成为新的 Leader。(基于zookeeper的选举机制。)

生产者

生产者发送流程:

consumer注册不到kafka 上 kafka consumer assign_kafka

  1. 生产者主线程调用send()方法,发送数据。
  2. 数据首先经过拦截器 Interceptors 然后经过序列化器序列化 最后到达分区器。
  3. 分区器会通过分区的规则将每个分区的数据发送到一个双端的队列上。
  4. 双端队列一个批次的数据到达batch.size=16k 或到达指定的等待时间linger.ms时 会将数据发送到sender线程。【sender最多缓存5个批次的数据】
  5. sender线程通过Selector 将数据发送到 Broker上。Broker 会根据应答机制acks设置值进行某种程度的应答返回。
  6. 应答成功 清除sender线程中和队列中的数据。应答失败,不断重试,直到发送成功。

发送数据有同步发送机制和异步发送机制:
同步发送,需要等待ack然后再进行接下来的发送。
异步发送,不需要等待ack就能进行接下来的发送。

这样,同步发送,会比较安全,速度慢,但是减少了丢失数据的风险。异步发送则相反。

数据不丢失

在什么场景会丢失数据[从宏观到细致]:

  1. 异步发送速度过快,消费端消费的速度太慢。而发送端不断的发送导致内存溢出。
  2. 消费端提交offset的逻辑会影响数据是否丢失。
  3. acks的机制,涉及到数据发送到Broker获得响应的深度,数据是否被安全保存。

acks参数设置直接影响到数据是否被安全,即关系到发送端数据的丢失情况:

  • acks: 0 发送端发送过来的数据,不需要等待数据落盘应答,可以继续发送下一批次数据。
  • acks: 1发送端发送过来的数据,需要等待Broker Leader 落盘应答,然后可以继续发送下一批次数据。
  • acks: -1发送端发送过来的数据,需要等待所有ISR记录的节点落盘应答 ,然后可以继续发送下一批次数据。数据丢失的可能性不断的降低。

ISR: Leader + 通讯正常的Follower (in-sync replica set 同步复制集)

消费端提交offset的逻辑:

  1. 先提交offset, 程序异常没有消费,会产生丢失数据。
  2. 先消费数据,没有提交offset会产生重复消费。
  3. 消费和提交offset 是原子性的时候,才能保证数据不丢失。

精准消费

如何实现精确消费一次呢?

数据不允许丢失的场景
精准一次: 数据不丢失, 不重复。

数据不丢失: ack=-1 + 分区副本数>=2 + ISR最小副本数量>=2

这个配置来源于极致探讨ack=-1数据丢失的情况
如果细研究acks:-1 的丢失数据的可能性,只能在没有副本的情况下,和1的情况是相同的。以及ISR最小副本数量=1,代表只有一个Leader存活的情况下。此Leader挂掉就可能造成数据丢失。
所以得到配置: 分区副本数>=2 + ISR最小副本数量>=2

数据不重复: 幂等性
生产者无论发送多少重复数据到Broker,Broker只会持久化一次。
如何标识数据,检查是否重复呢?

<PID, Partition, SeqNumber> 作为唯一数据标识

开启幂等性:

# 开启幂等性 默认开启
enable.idempotence=true

Broker的工作原理

借鉴网上的一副图:

consumer注册不到kafka 上 kafka consumer assign_数据_02


Broker的工作原理:

  1. Broker 对应到集群中的一个服务节点。Broker启动会首先像Zookeeper中注册自己的ID
  2. 注册的Broker会抢先注册Controller,并成为整个集群的管理者 <- Controller 的选举方式
  3. 选举出来的Controller会监听brokers节点的变化
  4. Controller决定Leader选举 选举规则如图
  5. Controller 将节点信息 Leader Follower 信息上传到ZK。
  6. 其他节点从ZK同步所有节点的相关信息。
  7. 如果Broker中Leader挂了,Broker从isr中移除。
  8. Controller监听到节点的变化。
  9. 获取ISR 然后选举出新的Leader 按照AR靠前的方式选举。
  10. Controller更新到ZK中节点的信息,同时其他节点从ZK中拉取相关的节点信息。作为Topic-Leader的节点要担起读写的责任。

AR: KafKa中所有副本的统称

需要注意的关键点:

  1. Leader只是作为Topic-Leader的概念。不能抛开Topic谈Leader.
  2. 整个集群的控制节点在Controller,功能在每个Broker中,抢占这个位置做这个功能。

KafKa文件的存储机制

文件存储格式: .index .log .timeindex } 统称为segment

将每个分区分成多个segment,查询数据首先会定位分区,定位segment
.index 为稀疏索引,大约向log中写入4kb左右数据时候,会向.index写入一条索引。查询数据定位segment后会定位index。然后定位到具体的.log文件。

kafka会利用PageCache操作系统的页缓存,将尽可能多的空闲内存当作磁盘缓存来用,加速数据的寻址。

消费者组的初始化

consumer注册不到kafka 上 kafka consumer assign_consumer注册不到kafka 上_03


消费者的详细消费流程:

consumer注册不到kafka 上 kafka consumer assign_kafka_04


如何应对数据积压的问题:

  1. 提高分区数和消费者组中消费者的数量 增大并发。
  2. 如果采用拉取的方式消费, 增大拉取的数据量。