Kafka
2023-03-27 15:19:44 0 举报
AI智能生成
对于kafka组件的详细总结,绝对是最重点最核心的部分!超赞
作者其他创作
大纲/内容
架构
Broker
每个kafka server称为一个Broker,多个borker组成Kafka Cluster
一个broker可以维护多个topic
一个broker可以维护多个topic
Controller
Controller表示控制器,Kafka节点中的主节点。集群中任意一台Broker都能充当控制器的角色,但是,在运行过程中,只能有一个Broker成为控制器,行使其管理和协调的职责。
broker管理(新增broker、broker主动关闭、broker故障)
topic管理(创建主题、删除主题)
partition管理(leader分区选举、增加分区、rebalance分区)
值得注意的是:kafka集群中始终只有有一个controller broker
broker管理(新增broker、broker主动关闭、broker故障)
topic管理(创建主题、删除主题)
partition管理(leader分区选举、增加分区、rebalance分区)
值得注意的是:kafka集群中始终只有有一个controller broker
message
message表示消息,通过kafka集群进行传递的消息对象实体,存储需要传送的信息
message是实际发送和订阅的信息的实际载体,producer发送到kafka集群中的每条消息,都被kafka包装成了一个个message对象,之后再存储在磁盘中,而不是直接存储的。
message是实际发送和订阅的信息的实际载体,producer发送到kafka集群中的每条消息,都被kafka包装成了一个个message对象,之后再存储在磁盘中,而不是直接存储的。
topic
每条发布到kafka集群的消息都有一个类别,这个类别被称为topic,类似于数据库的table或者es的index
逻辑上一个topic的消息虽然保存于一个或多个broker上但用户只需指定消息的topic即可(实际上生产或消费数据不必关系数据存于何处)
producer将消息推送到topic,由订阅该topic扽consumer从topic中拉取消息
逻辑上一个topic的消息虽然保存于一个或多个broker上但用户只需指定消息的topic即可(实际上生产或消费数据不必关系数据存于何处)
producer将消息推送到topic,由订阅该topic扽consumer从topic中拉取消息
partition
kafka中topic被分成多个partition分区,每个topic至少有一个partition。多分区可以提高topic的效率
topic是一个逻辑概念,partition是最小的存储单元吞吐量和并行度,拥有着一个topic的部分数据。
每个partition都是一个单独的log文件,每条记录都以追加的形式写入
当生产者产生数据的时候,根据分配策略从选择分区,然后将消息追加到指定的分区的末尾
每条消息都会有一个自增的编号(offset偏移量):他表示分区中每条消息的位置信息,是一个单调递增且不变的值。换句话说,offset可以用来唯一的标识分区中每一条记录。每个partition都有自己独立的编号。partition中的数据是有序的,不同的partition间的数据是无序的
如果topic有多个partition,消费数据时就不能保证数据的顺序。严格保证消息的消费顺序的场景下,需要将partition数据设为1.
topic是一个逻辑概念,partition是最小的存储单元吞吐量和并行度,拥有着一个topic的部分数据。
每个partition都是一个单独的log文件,每条记录都以追加的形式写入
当生产者产生数据的时候,根据分配策略从选择分区,然后将消息追加到指定的分区的末尾
每条消息都会有一个自增的编号(offset偏移量):他表示分区中每条消息的位置信息,是一个单调递增且不变的值。换句话说,offset可以用来唯一的标识分区中每一条记录。每个partition都有自己独立的编号。partition中的数据是有序的,不同的partition间的数据是无序的
如果topic有多个partition,消费数据时就不能保证数据的顺序。严格保证消息的消费顺序的场景下,需要将partition数据设为1.
replication
数据会存放到topic的partation中,partation会存放到Broker上,但是有可能会因为Broker损坏导致数据的丢失
Kafka为一个Partition生成多个副本,并且把它们分散在不同的Broker上(就是说需要对数据进行备份,备份多少取决于你对数据的重视程度)。
如果一个Broker故障了,Consumer可以在其他Broker上找到Partition的副本,继续获取消息。
我们将分区的分为Leader(1)和Follower(N-1),备份数设置为N,表示主+备=N(参考HDFS)
Leader负责写入和读取数据
Follower只负责备份
保证了数据的一致性
Leader
每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。
Follower
Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,
Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。
Kafka为一个Partition生成多个副本,并且把它们分散在不同的Broker上(就是说需要对数据进行备份,备份多少取决于你对数据的重视程度)。
如果一个Broker故障了,Consumer可以在其他Broker上找到Partition的副本,继续获取消息。
我们将分区的分为Leader(1)和Follower(N-1),备份数设置为N,表示主+备=N(参考HDFS)
Leader负责写入和读取数据
Follower只负责备份
保证了数据的一致性
Leader
每个partition有多个副本,其中有且仅有一个作为Leader,Leader是当前负责数据的读写的partition。
Follower
Follower跟随Leader,所有写请求都通过Leader路由,数据变更会广播给所有Follower,
Follower与Leader保持数据同步。如果Leader失效,则从Follower中选举出一个新的Leader。
producer
生产者即数据的发布者,该角色将消息发布到kafka的topic中
broker接收到生产者发送的消息后,broker将该消息追加到当前用于追加数据的segment文件中。
broker接收到生产者发送的消息后,broker将该消息追加到当前用于追加数据的segment文件中。
consumer
根据offset依次从topic的partition中拉取消息。
consumer group
消费者组为了将多个消费者集中到一起去处理某一个topic的数据,可以提高数据的消费能力
整个消费者组共享一组偏移量,因为一个topic中有多个分区,每个分区都有自己的偏移量,防止数据被重复读取
整个消费者组共享一组偏移量,因为一个topic中有多个分区,每个分区都有自己的偏移量,防止数据被重复读取
offset
消息写入的时候,每一个分区都有一个offset,这个offset就是生产者的offset,同时也是这个分区的最新最大的offset
偏移量可以唯一的标识一条消息,且可以决定读取数据的位置,消费者通过偏移量来决定下次读取的消息。我们某一个业务也可以通过修改偏移量达到重新读取消息的目的,偏移量由用户控制。这其中不会有线程安全的问题,因为消息被消费之后,并不会被马上删除。这样还可以让多个业务重复使用kafka的消息,但是消息还是会被删除,默认生命周期为1周
偏移量可以唯一的标识一条消息,且可以决定读取数据的位置,消费者通过偏移量来决定下次读取的消息。我们某一个业务也可以通过修改偏移量达到重新读取消息的目的,偏移量由用户控制。这其中不会有线程安全的问题,因为消息被消费之后,并不会被马上删除。这样还可以让多个业务重复使用kafka的消息,但是消息还是会被删除,默认生命周期为1周
zookeeper
保存了所有的broker id,实现了对broker的动态监控/保存了所有topic
每个broker启动时,都会完成broker注册的过程,生产者会通过该节点的变化来动态地感知到broker服务器列表的变更
kafka早期版本使用zookeeper为每个消费者存储offset,由于zookeeper写入性能较差,从0.10版本后,kafka使用自己的内部主题维护offset
每个broker启动时,都会完成broker注册的过程,生产者会通过该节点的变化来动态地感知到broker服务器列表的变更
kafka早期版本使用zookeeper为每个消费者存储offset,由于zookeeper写入性能较差,从0.10版本后,kafka使用自己的内部主题维护offset
分区分配策略
RangeAssignor
RangeAssignor策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分
区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。对于每一个topic,
RangeAssignor策略会将消费组内所有订阅这个topic的消费者按照名称的字典序排序,然后为每
个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分
区。
缺陷
可以明显的看到这样的分配并不均匀,如果将类似的情形扩大,有可能会出现部分消费者过
载的情况
区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。对于每一个topic,
RangeAssignor策略会将消费组内所有订阅这个topic的消费者按照名称的字典序排序,然后为每
个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分
区。
缺陷
可以明显的看到这样的分配并不均匀,如果将类似的情形扩大,有可能会出现部分消费者过
载的情况
RoundRobinAssignor
RoundRobinAssignor策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的
partition按照字典序排序,然后通过轮询消费者方式逐个将分区分配给每个消费者。
消费者订阅相同 Topic
如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobinAssignor
策略的分区分配会是均匀的
消费者订阅不同 Topic
如果同一个消费组内的消费者所订阅的Topic 是不相同的,那么在执行分区分配的时候
就不是完全的轮询分配,有可能会导致分区分配的不均匀。
如果某个消费者没有订阅消费组内的某个topic,那么在分配分区的时候此消费者将分配
不到这个topic的任何分区
partition按照字典序排序,然后通过轮询消费者方式逐个将分区分配给每个消费者。
消费者订阅相同 Topic
如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobinAssignor
策略的分区分配会是均匀的
消费者订阅不同 Topic
如果同一个消费组内的消费者所订阅的Topic 是不相同的,那么在执行分区分配的时候
就不是完全的轮询分配,有可能会导致分区分配的不均匀。
如果某个消费者没有订阅消费组内的某个topic,那么在分配分区的时候此消费者将分配
不到这个topic的任何分区
StickyAssignor
StickyAssignor策略,“sticky”这个单词可以翻译为“粘性的”,Kafka从0.11.x版本开始引入这种分
配策略
它主要有两个目的:
① 分区的分配要尽可能的均匀;
② 分区的分配尽可能的与上次分配的保持相同。
③ 当两者发生冲突时,第一个目标优先于第二个目标。
配策略
它主要有两个目的:
① 分区的分配要尽可能的均匀;
② 分区的分配尽可能的与上次分配的保持相同。
③ 当两者发生冲突时,第一个目标优先于第二个目标。
数据不丢失
消息确认机制
为保证producer发送的数据,能够可靠地发送到指定地topic,topic地每个partition收到producer发送地数据后,都需要向producer发送ACKnowledge确认收到,如果producer收到ACK,就会进行下一轮地发送,否则重新发送数据
ACK机制
acks=0
这意味着producer无需等待来自Leader的确认而继续发送下一批消息
当broker故障时有可能丢失数据
当broker故障时有可能丢失数据
acks=1
producer在ISR中的leader已成功的收到数据并得到确认后发送下一条message
如果在follower同步成功之前leader故障,那么将会丢失数据
如果在follower同步成功之前leader故障,那么将会丢失数据
acks=-1
producer需要等待ISR中的所有follower都确认收到数据后才算一次发送完成,可靠性最高
在broker发送ack时,leader发生故障,则会造成数据重复。
在broker发送ack时,leader发生故障,则会造成数据重复。
精确一次消费
at most once
最多一次消费,数据可能会丢失,但是不会产生数据被重复消费的情况,该层语义的可靠性最低,ack级别设置为0
at least once
至少一次消费,数据肯定不会丢失,但是肯能会导致数据被重复的处理。ack级别设置为all,分区副本数大于等于2,ISR里应答的最小副本数大于等于2
exactly once
精确一次消费,对于一些非常重要的信息,数据不仅不会丢失,也不会被重复的处理。该层语义的基础是at least once+幂等性
注意
Kafka 0.11 版本以后,引入了一项重大特性:幂等性和事务,用于实现消息的精确消费。
幂等性就是指 Producer 不论向 Broker 发送多少次重复数据,Broker 端都只会持久化一条,保
证了数据的不重复。
幂等性就是指 Producer 不论向 Broker 发送多少次重复数据,Broker 端都只会持久化一条,保
证了数据的不重复。
数据不重复
生产端
为了实现producer的幂等性,kafka引入了producer ID(即PID)和Sequence Number
PID:每个新的producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
Sequence Number:对于每个pid,该producer发送数据的每个<topic,partition>都对应一个从0开始单调递增的sequence number
broker端在缓存中保存了这个sequence number,对于接收的每条消息,如果其序号大于broker缓存中序号则接受它,否则将其丢弃。这样就可以避免重复
这种情况只能保证单个producer对于同一个<topic,partition>的精准一次语义,不能保证同一个producer一个topic不同的partition幂等
PID:每个新的producer在初始化的时候会被分配一个唯一的PID,这个PID对用户是不可见的。
Sequence Number:对于每个pid,该producer发送数据的每个<topic,partition>都对应一个从0开始单调递增的sequence number
broker端在缓存中保存了这个sequence number,对于接收的每条消息,如果其序号大于broker缓存中序号则接受它,否则将其丢弃。这样就可以避免重复
这种情况只能保证单个producer对于同一个<topic,partition>的精准一次语义,不能保证同一个producer一个topic不同的partition幂等
消费端
数据落库的时候,根据主键去过滤
在落库时,如果不存在这条数据,则去新增,如果存在则去修改,如果不能幂等处理,则将consumer的提交方式设置为同步提交,是最大程度地保证一致性地方法,缺点是性能会降低很多
传递一次。将offset作为唯一id与消息同时处理,并且保证处理的原子性。消息只会处理一次,不丢失也不会重复。但是这种方式很难做到
使用exactly once+幂等操作,可以保证数据不重复、不丢失
在落库时,如果不存在这条数据,则去新增,如果存在则去修改,如果不能幂等处理,则将consumer的提交方式设置为同步提交,是最大程度地保证一致性地方法,缺点是性能会降低很多
传递一次。将offset作为唯一id与消息同时处理,并且保证处理的原子性。消息只会处理一次,不丢失也不会重复。但是这种方式很难做到
使用exactly once+幂等操作,可以保证数据不重复、不丢失
幂等性
事务性
事务意义
与幂等性有关的另一个特性就是事务,前面我们提到幂等性只能保证在单分区内不重复,所以幂等性不能跨多个分区运作,而事务可以弥补这个缺陷,事务可以保证对多个分区写入操作的原子性。操作的原子性是指多个操作要么全部成功,要么全部失败,不存在部分成功、部分失败的可能。
kafka中的事务与数据库的事务类似,kafka中的事务属性是指一系列的producer生产消息和消费消息提交offset的操作在一个事务中,即原子性操作。对应的结果是同时成功或者同时失败,这里需要与数据库中事务进行区别,操作数据库中的事务指一系列的增删查改,对kafka来说,操作事务是指一系列的生产和消费等原子操作
为了使用事务,Producer 必须显式设置唯一的 transactional.id(将 transactional.id 参数设置为非空从而开启事务),默认值为 null,并将 Producer 获得的 PID 和 Transaction ID 绑定。这样当 Producer 重启后就可以通过正在进行的 Transaction ID 获得原来的 PID。同时还需要将 enable.idempotence 设置为 true(因为使用事务生产者必须开启幂等性),如果用户显式地将 enable.idempotence 设置为 false,会抛出 ConfigException 的异常。
这里还有一个概念叫做拒绝僵尸实例(Zombie fencing):为了保证新的生产者启动后具有相同 TransactionalId 的旧生产者能够立即失效,每个生产者通过 TransactionalId 获取 PID的同时,还会获取一个单调递增的 ProducerEpoch。如果使用同一个 TransactionalId 开启两个生产者,Kafka 收到事务提交请求时检查当前事务提交者的 epoch 不是最新的,那么就会拒绝该 Producer 的请求,从而达成拒绝僵尸实例的目标。
kafka中的事务与数据库的事务类似,kafka中的事务属性是指一系列的producer生产消息和消费消息提交offset的操作在一个事务中,即原子性操作。对应的结果是同时成功或者同时失败,这里需要与数据库中事务进行区别,操作数据库中的事务指一系列的增删查改,对kafka来说,操作事务是指一系列的生产和消费等原子操作
为了使用事务,Producer 必须显式设置唯一的 transactional.id(将 transactional.id 参数设置为非空从而开启事务),默认值为 null,并将 Producer 获得的 PID 和 Transaction ID 绑定。这样当 Producer 重启后就可以通过正在进行的 Transaction ID 获得原来的 PID。同时还需要将 enable.idempotence 设置为 true(因为使用事务生产者必须开启幂等性),如果用户显式地将 enable.idempotence 设置为 false,会抛出 ConfigException 的异常。
这里还有一个概念叫做拒绝僵尸实例(Zombie fencing):为了保证新的生产者启动后具有相同 TransactionalId 的旧生产者能够立即失效,每个生产者通过 TransactionalId 获取 PID的同时,还会获取一个单调递增的 ProducerEpoch。如果使用同一个 TransactionalId 开启两个生产者,Kafka 收到事务提交请求时检查当前事务提交者的 epoch 不是最新的,那么就会拒绝该 Producer 的请求,从而达成拒绝僵尸实例的目标。
事务用途
使用场景:
1、生产者发送多条消息可以封装在一个事务中,形成一个原子操作。多条消息要么都发送成功,要么都发送失败
2、read-process-write模式:将消息消费和生产封装在一个事务中,形成一个原子操作。在一个流式处理的应用中,常常一个服务需要从上游接收消息,然后经过处理后送达到下游,这就对应着消息的消费和生成。
从生产者的角度分析,通过事务,Kafka 可以保证跨生产者会话的消息幂等发送,以及跨生产者会话的事务恢复。前者表示具有相同 TransactionalId 的新生产者实例被创建且工作的时候,旧的且拥有相同 TransactionalId 的生产者实例将不再工作。后者指当某个生产者实例宕机后,新的生产者实例可以保证任何未完成的旧事务要么被提交(Commit),要么被中止(Abort),如此可以使新的生产者实例从一个正常的状态开始工作。
1、生产者发送多条消息可以封装在一个事务中,形成一个原子操作。多条消息要么都发送成功,要么都发送失败
2、read-process-write模式:将消息消费和生产封装在一个事务中,形成一个原子操作。在一个流式处理的应用中,常常一个服务需要从上游接收消息,然后经过处理后送达到下游,这就对应着消息的消费和生成。
从生产者的角度分析,通过事务,Kafka 可以保证跨生产者会话的消息幂等发送,以及跨生产者会话的事务恢复。前者表示具有相同 TransactionalId 的新生产者实例被创建且工作的时候,旧的且拥有相同 TransactionalId 的生产者实例将不再工作。后者指当某个生产者实例宕机后,新的生产者实例可以保证任何未完成的旧事务要么被提交(Commit),要么被中止(Abort),如此可以使新的生产者实例从一个正常的状态开始工作。
高吞吐本质
顺序写入
分页缓存
零拷贝技术
为什么快
页缓存技术+磁盘顺序写
首先kafka每次接受到数据都会往磁盘上去写。操作系统本身有一层缓存,叫做page cache,是在内存里的缓存,我们也可以称之为os cache,意思就是操作系统自己管理的缓存。在数据写入磁盘文件的时候,可以直接写入这个os cache里,也就是仅仅写入内存中,接下来由操作系统自己决定什么时候把os cache里的数据真的刷入磁盘文件中。
接着就是kafka写数据的时候,它是以磁盘顺序写的方式来写的。也就是说,仅仅将数据追加到文件的末尾,不是在文件的随机位置来修改数据
接着就是kafka写数据的时候,它是以磁盘顺序写的方式来写的。也就是说,仅仅将数据追加到文件的末尾,不是在文件的随机位置来修改数据
一般的流程
页缓存技术
零拷贝技术
kafka消费数据的时候实际上就是要从kafka的磁盘文件里读取某条数据然后发送给下游的消费者
发生无用拷贝的过程:消费者首先看看要读的数据在不在os cache里,如果不在的话就从磁盘文件里读取数据后放入os cache。接着从操作系统的os cache里拷贝数据到应用程序进程的缓存里,再从应用程序进程的缓存里拷贝数据到操作系统层面的Scoket缓存里,最后从socket缓存里提取数据后发送到网卡,最后发送出去给下游消费
通过零拷贝技术,就不需要把os cache里的数据拷贝到应用缓存,再从应用缓存拷贝到socket缓存了。首先看os cache内存中是否有,如果有的话,其数据都是直接读内存,完全基于内存读写,提高整体的性能
发生无用拷贝的过程:消费者首先看看要读的数据在不在os cache里,如果不在的话就从磁盘文件里读取数据后放入os cache。接着从操作系统的os cache里拷贝数据到应用程序进程的缓存里,再从应用程序进程的缓存里拷贝数据到操作系统层面的Scoket缓存里,最后从socket缓存里提取数据后发送到网卡,最后发送出去给下游消费
通过零拷贝技术,就不需要把os cache里的数据拷贝到应用缓存,再从应用缓存拷贝到socket缓存了。首先看os cache内存中是否有,如果有的话,其数据都是直接读内存,完全基于内存读写,提高整体的性能
未使用零拷贝
使用零拷贝
0 条评论
下一页