消息队列 随笔 5-kafka深入

0. 我想玩玩小块儿地…

看完这篇Kafka,你也许就会了Kafka

1. 分区 Paritition

为了实现扩展性,一个非常大的Topic可以分布到多个Broker上,一个Topic可以分为多个Partition,每个Partition是一个有序的队列(分区有序,不能保证全局有序)

1.1 分区底层关系

Topic是逻辑上的概念,Partition是物理上的概念,每个Partition对应着一个log文件,该log文件中存储的就是producer生产的数据

1.1.1 分区数据的持久化

  • Kafka文件存储也是通过本地落盘的方式存储的,主要是通过相应的log与index等文件保存具体的消息文件
    逻辑上,log由多个分片(segment)构成
  • 1个分片(segment)由.index、.log文件构成(分片和索引的机制快速定位大文件——二分查找)
    .index文件存储的消息的offset+真实的起始偏移量
    .log中存放的是真实的数据。

1.2 分区 与 生产/消费

  • Producer生产的数据会被不断的追加到该log文件的末端,且每条数据都有自己的offset
  • consumer组中的每个consumer,都会实时记录自己消费到了哪个offset,以便出错恢复的时候,可以从上次的位置继续消费。

1.3 分区内部结构

1.3.1 Leader

每个分区多个副本的主角色,生产者发送数据的对象,以及消费者消费数据的对象都是Leader。

1.3.2 Follower

每个分区多个副本的从角色,实时的从Leader中同步数据,保持和Leader数据的同步,Leader发生故障的时候,某个Follower会成为新的Leader。

1.4 分区分配规则

1.4.1 消息产出时的分区分配规则

  1. 由消息的生产者指定分区
  2. 上述条件不存在时,若消息有Key,则取 key的hash对分区总数 取模
  3. 上述条件不存在时,第一次调用时产生一个随机数,后续调用时在这个随机上++,然后将其对分区数取模(round-robin算法)

1.4.2 消息被消费时的分区分配规则

1.4.2.1 先解释一波为什么消费方式:Pull
  • kafka消费消息采用pull的方式(非push)的好处:
    不同消费者的消费速率可能不同
    push的速率由broker来决定

  • kafka缓解的pull所带来的空轮询的性能损耗:
    消费者支持传入一个timeout

1.4.2.2 进入正题

一个cusomer group中如何决定某条消息由哪个实例消费:

  • round robin

请添加图片描述

做法
	这里并未使用随机数,而是按照分区的字典值 对 分区、消费者实例 排序
	循环遍历,遇到自己订阅的topic则消费,否则向下轮询
坏处
	订阅多的消费者实例,压力大,总的说就是不均衡
  • range

请添加图片描述

做法:
	topicX-PartitionY按先topic后partition的方式分配
	如果多出1个则分配给字典值靠前的 消费者实例
坏处
	如果topic多的话,字典值靠前的 消费者实例 可能压力大

2. 数据可靠性

2.1 客户端提交消费

消费者每次调用poll()方法,它总是返回由生产者写入Kafka但没有被消费者读取过的记录,我们因此可以知道哪些消息是被群组里的哪个消费者读取的。Kafka不会像JMS队列那样需要得到消费者的确认,消费者使用broker里的MetaData来追踪消息在分区里的位置(offset)

提交方式:自动提交、手动提交(又分为 同步、异步)

2.1.1 自动提交

定时

2.1.2 手动提交

2.1.2.1 同步方式

2.1.2.2 异步方式

相比 同步 多了一个 回调方法

但二者都可能出现 漏消费、重复消费(由rebalance引起):
    提交offset后消费,有可能造成数据的漏消费,而先消费再提交offset,有可能会造成数据的重复消费

2.1.3 rebalance

2.1.3.1 什么是 rebalance
  • 当有新的消费者加入消费者组、已有的消费者退出消费者组或者订阅的主体分区发生了变化,会触发分区的重新分配操作,重新分配的过程称为Rebalance。

  • 消费者发生Rebalace之后,每个消费者消费的分区就会发生变化,因此消费者需要先获取到重新分配到的分区,并且定位到每个分区最近提交的offset位置继续消费。(High Water高水位)

2.1.3.2 如何解决 rebalance 问题
  • rebalance发生前回调 -> 先将offset提交
  • rebalance发生之后回调 -> 找到最新的offset位置继续消费即可

2.1.4 消费者保存offset的方式

  • Kafka0.9版本之前,offset存储在zookeeper中,0.9版本及之后的版本,默认将offset存储在Kafka的一个内置的topic中(_consumer_offsets),以便故障恢复后继续消费。除此之外,Kafka还可以选择自定义存储offset数据。offse的维护相当繁琐,因为需要考虑到消费者的rebalance过程

  • 消费者会向_consumer_offset的特殊主题发送消息,消息里包含每个分区的offset。如果消费者一直处于运行状态,offset就没什么用处,用处体现在发生rebalance的时候

2.2 生产者

2.2.1 生产者动态ISR(in-sync replica set,副本集同步策略)

“动态” 指的是:
follower长时间没有向leader同步数据,则将其从ISR中踢出

2.2.2 生产者的ack

对于某些不太重要的数据,对数据的可靠性要求不是很高,能够容忍数据的少量丢失,所以没有必要等到ISR中所有的follower全部接受成功。

kafka 提供了 三种级别的 ack

0 	
	producer不等待broker的ack,这一操作提供了最低的延迟,broker接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据
1
	producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将丢失数据。(只是leader落盘)
-1
	producer等待broker的ack,partition的leader和ISR的follower全部落盘成功才返回ack,但是如果在follower同步完成后,broker发送ack之前,如果leader发生故障,会造成数据重复。(这里的数据重复是因为没有收到,所以继续重发导致的数据重复)

3. 数据一致性

3.1 副本集中leader与followers的一致性

3.1.1 引入的概念

  • LEO(Log End Offset):每个副本末端的偏移量
  • HW(High Watermark):高水位,指代消费者能见到的最大的offset,ISR队列中最小的LEO。

请添加图片描述

3.1.2 如果解决follower和leader故障

  • follower故障:
  1. 踢出ISR
  2. follower恢复
  3. follower截掉故障前HW后面的部分
  4. follower重新同步leader
  5. follower追上leader后,再次加入到ISR

  • leader故障:
  1. ISR的followers中重新选举出一个新的leader
  2. 其余follower将各自log中HW后面的部分截掉
  3. 截掉后,重新向新的leader同步

这些个做法只能保证副本间的数据一致性,无法保证数据 不漏 或 不重

3.1.3 ExactlyOnce:解决不漏、不重的问题

At least Once:生产者ack=-1
at most once:生产者ack=0

3.1.3.1 具体实现方案

借组 kafka-v0.11引入 幂等性:

  • 需要手动开启
  • 将去重的操作放到了上游做(给生产者分配一个PID,对PDI&Partition&SeqNumber去重)
  • 配合 at least once的语义,可以做到 exactly once

4. 高性能读写

  • Kafka的producer生产数据,需要写入到log文件中,写的过程是追加到文件末端,顺序写的方式

  • 底层借助 零拷贝技术

5. Zookeeper的作用

Kafka集群中有一个broker会被选举为Controller,Controller的工作管理是依赖于zookeeper的:

  1. 负责管理集群broker的上下线
  2. 所有topic的分区副本分配和leader的选举等工作。

请添加图片描述

6. 事务

kafka从0.11版本开始引入了事务支持,事务可以保证Kafka在Exactly Once语义的基础上,生产和消费可以跨分区的会话,要么全部成功,要么全部失败。

6.1 Producer事务

为了按跨分区跨会话的事务,需要引入一个全局唯一的Transaction ID,并将Producer获得的PID(可以理解为Producer ID)和Transaction ID进行绑定,这样当Producer重启之后就可以通过正在进行的Transaction ID获得原来的PID。

为了管理Transaction,Kafka引入了一个新的组件Transaction Coordinator,Producer就是通过有和Transaction Coordinator交互获得Transaction ID对应的任务状态,Transaction Coordinator还负责将事务信息写入内部的一个Topic中,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以恢复,从而继续进行。

也会保存在内置的topic上…

6.2 Consumer事务

对于Consumer而言,事务的保证相比Producer相对较弱,尤其是无法保证Commit的信息被精确消费,这是由于Consumer可以通过offset访问任意信息,而且不同的Segment File声明周期不同,同一事务的消息可能会出现重启后被删除的情况。

7. API生产者流程

Kafka的Producer发送消息采用的是异步发送的方式,在消息发送的过程中,设计到了两个线程main线程和Sender线程,以及一个线程共享变量RecordAccumulator,main线程将消息发送给RecordAccumulator,Sender线程不断从RecordAccumulator中拉取消息发送到Kafka broker中。


就是一个buff:

请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值