Kafka

一、异步通信原理

1.1 观察者模式

观察者模式又叫,发布订阅模式

定义对象间有一种一对多的关系,使得当一个对象改变状态,则与它关联的对象都会得到通知并自动更新

一个对象(目标对象)的状态发生改变,所有依赖的对象(观察者对象)都将得到通知

现实生活中的应用场景

京东到货通知

1.2 生产者消费者模式

传统模式:

生产者直接将消息传递给指定的消费者

耦合性特别高,当生产者或消费者发生变化的时候,都需要重写业务逻辑

生产者消费者模式

通过一个容器来解决生产者消费者的强耦合问题,生产者消费者之间不直接通讯,而时通过阻塞队列来进行通讯。

数据传输流程

N个线程生产消息,N个线程消费消息,俩中角色通过缓冲区进行通信,生产者负责向缓冲区调价数据单元,消费者负责从缓冲区取出数据单元,一般遵循先进先出原则。

1.3 缓冲区

解耦

假设生产者和消费者时俩个类。如果让生产者直接调用消费者的某个方法,那么生产者对消费者就会产生依赖。

支持并发

生产者直接调用消费者某个方法过程中函数调用是同步的

玩意消费者处理数据很慢,生产者就会白白糟蹋大好时光

支持忙闲不均

当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中

1.4 数据单元

关联到业务对象

数据单元必须关联到某种业务对象

完整性

传输的过程中保证该数据单元的完整

独立性

各个数据单元没有依赖

某个数据单元传输失败不会影响其他已经完成传输的数据单元;也不影响尚未传输的数据单元

颗粒度

如果颗粒度过小会增加数据传输的次数

颗粒度过大会增加单个数据传输的时间,影响后期消费

二、消息系统原理

一个消息系统负责将一个数据从一个应用传递给另一个应用的时候,应用只需关注于数据,而不必关注数据在多个应用之间是如何传递的。

2.1 点对点的消息传递

在点对点的消息系统中,消息持久化到一个队列中。此时,将有一个或多个消费者消费队列中的数据,但是每个消息只会被消费一次。

当一个消费者消费了队列中的某条数据之后,该条数据则从消息队列中删除

该模式即使有多个消费者同时消费数据,也能保证数据处理的顺序

基于推送模型的消息系统,由消息代理记录消费状态。

消息代理将消息推送(push)到消费者后,标记这条消息为已经被消费,但是这种方式无法很好地保证消费处理语义

 2.2 发布订阅消息传递

在发布-订阅模式中,消息被持久化到一个topic中

消费者可以订阅一个或多个topic,消费者可以消费该topic中所有的数据,同一条数据可以被多个消费者消费,数据消费后不会立马删除

在订阅发布模型中,消息的生产者称为发布者,消费者称为订阅者

kafka采取拉取模型,由自己控制消费速度,消费者可以按照自己的偏移量进行消费

三、kafka

kafka由scala和java语言编写。是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。

kafka本质上是一个MQ(Message Queue),使用消息队列的好处

解耦:允许我们独立的修改或扩展队列俩变的处理过程

可恢复性:即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

缓冲:有助于解决生产消息和消费消息的处理速度不一致的情况。

灵活性&峰值处理能力:不会因为突发的超负荷的请求而完全崩溃,消息队列能够使关键组件顶住突发的访问压力。

异步通信:消息队列允许用户把消息放入队列但不立即处理它。

 3.1 设计目标

时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能保证常数时间的访问性能。

高吞吐率:即使在非常廉价的商用机器上也能做到单机支持每秒100K条消息的传输。

支持Kafka Server间的消息分区,及分布式消费,同时保证每个partition内的消息顺序传输。

同时支持离线数据处理和实时数据处理。

支持在线水平扩展

四、kafka的系统架构

 4.1 Broker

kafka集群包含一个或多个服务器,服务器节点称为broker

4.2 topic

每条发布到kafaka集群的消息都有一个类别,这个类别被称为topic

类似于数据库中的table或者ES的index

物理上不同的topic的消息分开来存储

逻辑上一个topic虽然保存于一个或多个broker上但用户只需指定消息的topic即可上产或消费数据,而不必担心数据存放在何处。

4.3 partition

 topic中的数据可以分割为一个或多个partition

每隔topic至少有一个partition,生产者消费数据的时候,根据分配策略,选择分区,然后将消息追加到指定的分区的末尾

每条消息都会有一个自增编号

标识顺序

用于标识消息的偏移量

每个partition都有自己独立的编号

每个partition中的数据使用多个segment文件存储

partition中的数据是有序的,但是不同partition件的数据丢失了数据的顺序

如果有多个partition,消费数据的时候就不能保证数据的顺序。严格保证数据顺序的场景下,需要将partition数据设为1.

4.4 Leader

每个partition有多个副本,其中有且仅有一个作为leader,leader是负责当前数据读写的partition

4.5 Follower

Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower

Follower与Leader保持数据同步

如果Leader失效,则从Follower中选举出一个Leader来

当Follower挂掉、卡慢,leader会把这个follower从 “in sync replicas”列表中删除,重新创建一个Follower

4.6 replication

数据会存放到topic中的partition中,但是有可能分区会损坏

我们需要对分区进行备份

我们将分区分为Leader和Follower

Leader只负责写入和读取数据

Follower只负责备份

保证了数据的一致性

4.7 Producer

生产者即数据的发布者,该角色将消息发布到kafka的topic中

broker接收到生产者发送的消息后,broker将消息追加到当前用于追加数据的segment文件中

生产者发送消息,存储到一个partition中,生产者,也可以指定数据存储的partiton

4.8 consumer

消费者可以从broker中读取数据。消费者可以消费多个topic中的数据

4.9 consumer group

每个consumer属于一个特定的consumer group

将多个消费者集中到一起去处理某一个topic,可以更快的提高数据的消费能力

整个消费者组共享一组偏移量,因为一个topic有多个分区

4.10 offset偏移量

可以标识唯一的消息

偏移量决定读取数据的位置,不会有线程安全的问题,消费者通过偏移量来决定下次读取的消息

消息被消费之后,并不被马上删除,这样多个业务就可以重复使用kafka的消息

我们某一个业务也可以通过修改偏移量达到重新读取消息的目的,偏移量由用户控制

消息最终还是会被删除的,默认生命周期为1周(7*24小时)

4.11 zookeeper

kafka用zookeeper来存储集群的meta信息

 

五、Kafka数据存储

 partition可以细分为Segment,一个Partition物理上有多个Segement组成

LogSegment文件由两部分组成,分别为“.index”文件和“.log”文件,分别表示为 Segment 索引文件和数据文件

Partition全局的第一个Segment从0开始,后续每个Segment文件名为上一个Segment文件最后一条消息的offset值

数值大小为64位,20位数字字符长度,没有数据用0填充

 消息都有固定的物理结构,包括:offset(8 Bytes)、消息体的大小(4 Bytes)crc32(4 Bytes)、magic(1 Byte)、attributes(1 Byte)key length(4 Bytes)key(K Bytes)payload(N Bytes)等等字段,可以确定一条消息的大小,即读取到哪里截止。

“.index”文件存储大量的索引信息,“.log” 文件存储大量的数据,索引文件中的元数据指向对应数据文件中Message 的物理偏移量。

 六、生产者数据安全

6.1 数据分区

分区原因:方便在集群中扩展,每个 Partition 可以通过调整以适应它所在的机器

分区原则: Producer 发送的数据封装成一个 ProducerRecord 对象。对象包含:

topic:string 类型,NotNull

partition:int 类型,可选。

timestamp:long 类型,可选。

key:string 类型,可选。

value:string 类型,可选。

headers:array 类型,Nullable

6.2 数据可靠性保证

6.2.1 ACK机制

为了保证生产者发送的数据是可靠的,Producer会接收来自Partition的ACK响应。

6.2.2 ACK时机

一种是部分Follower与Leader同步数据完成,Leader发送一个ACK。

另一种是全部Follower与Leader同步数据完成,Leader发送一个ACK。

Leader中维护着一个动态的ISR(ISR和Leader保持数据同步的Follower)

ISR中如果有副本长时间或者有多条数据(根据自己设定的条件来抉择)为同步则会被踢出ISR

Leader发生故障之后,就会从ISR会重新选举出Leader,选举第一个恢复的node作为Leader提供服务,这个措施被称为脏leader选举

6.2.3 ACK应答机制

acks=0:意味着producer无需等待leader的ACK即可发送下一条数据(效率最高,但不安全)

当Broker故障时可能丢失数据

acks=1:Producer在ISR中的leader发送ack确认之后发送下一条消息(效率、安全性适中)

在Follower同步完成之前leader故障,那么数据将会丢失

acks=-1:Producer要确认所有Follower都同步好数据之后才再次发送一次数据,安全性最高

在Broker发送ACK时,leader故障,会造成数据重复

6.2.4 故障处理

 LEO:每个副本最大的offset

HW:消费者可见的最大offset,ISR中最小LEO

Follower故障:

ISR中出现Follower故障后,会把当前故障Follower提出ISR,等待Follower故障恢复后,它会读取本地磁盘上记录的HW,并将log文件高于HW部分的截取掉,开始向leader同步数据等待该Follower的HW大于等于该partition的HW,即Follower追上leader时,它会再次进入ISR

Leader故障:

Leader发生故障之后,会在ISR中重新选举一个新的leader,之后为了保证数据的同步性,其余的Follower都会截取掉log文件高于HW的部分,然后从新的leader同步数据

这只能保证副本间的数据一致性,不能保证数据不丢失或不重复

6.2.5 Exactly Once的含义

ACK=-1:At Least Once 可以保证数据不丢失,但是不能保证数据不重复;

ACK=0:At Most Once 可以保证数据不重复,但是不能保证数据不丢失。

Exactly Once:At Least Once + 幂等性 = Exactly Once

开启幂等性后Producer在发送一条数据的时候会被分配一个PID,发往同一个Partition的消息会附带一个sequence number,而Broker会对<PID,Partition,Sequence number>做缓存,当具有相同主键的消息提交的时候,Broker只会持久化一条

七、消费者数据安全

7.1 消费方式

Consumer采用Pull拉取的模式从Broker读取数据

        Consumer如果采用Push推送的方式,Broker推送给Consumer,推送的效率时由Broker决定的,很难适应Consumer的消费效率

        它的目标是尽可能快的向Consumer推送消息,不考虑Consumer的消费速率,很容易造成Consumer来不及处理消息。

而Pull方式则是根据Consumer消费的效率来消费消息

pull的不足之处是,如果kafka没有数据,则消费者可能会陷入循环中,一直返回空数据

因为消费者从Broker主动拉取数据,需要维护一个长轮询,针对这一点,kafka的消费者在消费的时候会传入一个长参数timeout。

当kafka中没有数据可消费的时候,consumer会等待一段时间以后再返回,这段时间便是timeout。

7.2 分区分配策略

将分区的所有劝从一个消费者移动到另一个消费者称为重新平衡

区分分配时机

同一个consumer group中新增消费者

消费者离开当前consumer group

订阅的主题新增分区

kafka有三种分配策略

一个是RangeAssignor(默认)每个消费者分区内的消费者根据自己订阅的topic与订阅相同topic 的消费者平均分配分区,多出来的分区会被分配到靠前的消费者

缺陷:

可以明显的看到这样的分配并不均匀,如果将类似的情形扩大,有可能会出现部分消费者过载的情况

一个是RoundRobinAssignor 原理是将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询消费者方式逐个将分区分配给每个消费者。

如果同一个消费者组订阅同一个topic,则消息会被均匀分配

 消费者订阅不同的topic可能会导致消息分配不均匀,如果某个消费者没有订阅某个topic,那么该消费者将不会分配到这个topic的任意分区

一个是StickyAssignor

它主要有俩个目的

分区尽可能均匀

分区的分配尽可能的与上次分配的保持相同。

两者发生冲突时,第一个目标优先于第二个目标。

消费者订阅相同Topic:

 假设此时消费者C1脱离了消费组,那么消费组就会执行再平衡操作,进而消费分区会重新分配。

RoundRobinAssignor策略

 消费者订阅不同的topic

RoundRobinAssignor策略

 StickyAssignor策略

 消费者脱离消费组的情况 RoundRobin

消费者脱离消费组的情况 Sticky

 7.3 Offset

7.3.1 生产端offset

kafka接收到生产者发送来的消息实际上是以日志的形式保存在对应分区的磁盘上,每条消息都有一个offset来标识它在分区中的位置,每次写入都是追加到文件的末尾

第一条消息的Offset是0,最后一条消息offset为8,LEO为9,代表下一条消息将要被写入的位置。

消费者可见的只能是HW之前的数据 

7.3.2 消费端的Offset

消费者在消费消息的时候也会维护一个Offset,表示消费到分区中某个消息所在的位置

 上图中表示consumer已经消费完offset为8的数据,下次消费从offset为9的位置开始

消费者提交的offset维护在consumer_offset这个topic中,具体维护在哪个分区中,是由消费者所在的消费者组groupid决定的

计算方法是group的hashcode对50取余

消费者提交的offset可以自动提交也可以手动提交

也可以同步提交异步提交

同步提交会阻塞消费者线程直至提交完成

异步提交不会阻塞消费者线程,但是出现先提交的位移失败而后提交的位移成功了导致重复消费数据。对此可以设置递增的序号来维护异步提交顺序。

消费者消费时

如果没有对应的offset则会从分区末尾开始消费,如果设置为earliest表示从分区开始处消费,也可以指定offset处开始消费

消费者提交的offset也会被定期清除

存储位置

kafka0.9 版本之前,Consumer 默认将 Offset 保存在 Zookeeper 中, 0.9开始Consumer 默认将 Offset 保存在 Kafka 一个内置的 Topic 中,该 Topic 为__consumer_offsets

7.3.3 checkpoint的offset

kafka每一个数据根目录都会包含最基本的N个检查点文件和meta.properties文件

在创建topic的时候,如果当前broker中不止配置了一个data目录,那么会挑选分区数量最少的那 个data目录来完成本次创建任务。

每个checkpoint作用如下

recovery-point-offset-checkpoint

表示已经刷写到磁盘的offset信息,对应LEO信息。kafka中会有一个定时任务负责将所有分区的LEO刷写到恢复点文件recovery-point-offset-checkpoint中,定时周期由broker端参数log.flush.offset.checkpoint.interval.ms配置,默认值60000,即60s。Kafka在启动时会检查文件的完整性,如果没有.kafka_cleanshutdown这个文件,就会 进入一个recover逻辑,recover就是从此文件中的offset开始。

replication-offset-checkpoint

来存储每个replicaHW,表示已经被commitedoffset信息。失败的follower开始恢复时,会首先将自己的日志截断到上次的checkpointed时刻的HW,然后向leader拉取消息。kafka有一个定时任务负责将所有分区的HW刷写到复制点文件replication-offset-checkpoint中,定时周期由broker端参数

log-start-offset-checkpoint

对应logStartOffset,用来标识日志的起始偏移量。kafka中有一个定时任务负责将所有分区的logStartOffset刷写到起始点文件log-start-offset-checkpoint中,定时周期有 broker端参数log.flush.start.offset.checkpoint.interval.ms配置,默认值60000,即60s

cleaner-offset-checkpoint

存了每个log的最后清理offset

八、kafka的事务性

8.1 kafka的幂等性

kafka的幂等性就是为了保证单个分区不会重复写入数据,而实现幂等性就是引入了producer id和sequence number这俩个概念

判断流程

kafka内部会自动为每个Producer分配一个producer id,broker端会为每个partition维护一个<PID,Partition>->sequence number映射,sequence number开始从0递增

 如果新消息的sequence number正好是broker端维护的<PID,Partition> -> sequence number1,说broker会接受处理这条消息。

如果新消息的sequence numberbroker端维护的sequence number要小,说明时重复消 息,broker可以将其直接丢弃

如果新消息的sequence numberbroker端维护的sequence number要大过1,说明中间存在了丢数据的情况

8.2 kafka事务

写入多个Partition要么全部成功,要么全部失败,不会出现部分成功部分失败这种情况。

kafka的事务处理,主要是允许应用可以把消费和生产的 batch 处理(涉及多个 Partition)在一 个原子单元内完成,操作要么全部完成、要么全部失败。为了实现这种机制,我们需要应用能提供一个唯一 id,即使故障恢复后也不会改变,这个 id 就是 TransactionnalId(也叫 txn.id,后面会详细讲述),txn.id 可以跟内部的 PID 1:1 分配,它们不同的是 txn.id 是用户提供的,而 PID 是 Producer 内部自动生成的(并且故障恢复后这个 PID 会变化),有了 txn.id 这个机制,就可以实 现多 partition、跨会话的 EOS 语义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值