作者:sowhat0125 2021-02-01 20:35:49
开发
架构
Kafka Kafka 是一个分布式的基于发布/订阅模式的消息队列,依靠其强悍的吞吐量,Kafka 主要应用于大数据实时处理领域。在数据采集、传输、存储的过程中发挥着举足轻重的作用。
成都创新互联专注于阜城网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供阜城营销型网站建设,阜城网站制作、阜城网页设计、阜城网站官网定制、微信小程序开发服务,打造阜城网络公司原创品牌,更为您提供阜城网站排名全网营销落地服务。
本文转载自微信公众号「sowhat1412」,作者sowhat0125。转载本文请联系sowhat1412公众号。
1 Kafka 简介
1.1 Kafka 概述
Kafka架构
Kafka 是一个分布式的基于发布/订阅模式的消息队列,依靠其强悍的吞吐量,Kafka 主要应用于大数据实时处理领域。在数据采集、传输、存储的过程中发挥着举足轻重的作用。
1.2 Kafka 优点
1.3 Kafka 缺点
1.4 Kafka 架构
1.5 ZooKeeper 作用
ZooKeeper 在 Kafka 中有举足轻重的地位,一般提供如下功能:
1.5.1 Broker 注册
Broker 是分布式部署并且相互之间相互独立,但是需要有一个注册系统能够将整个集群中的Broker管理起来,比如用ZooKeeper。
1.5.2 Topic注册
在 Kafka 中同一个 Topic 的消息会被分成多个 Partition 并将其分布在多个 Broker 上,这些 Partition 信息及与 Broker 的对应关系也都是由 Zookeeper 在维护,由专门的节点来记录。
1.5.3 生产者负载均衡
同一个Topic消息会被分区并将其分布在多个Broker上,因此,生产者需要将消息合理地发送到这些分布式的Broker上。
老式的四层负载均衡,根据生产者的IP地址和端口来为其确定一个相关联的Broker。一般一个生产者只会对应单个Broker,但实际系统中的每个生产者产生的消息量及每个Broker的消息存储量都是不一样的。
使用 Zookeeper 进行负载均衡,由于每个Broker启动时,都会完成Broker注册过程,生产者会通过该节点的变化来动态地感知到Broker服务器列表的变更,这样就可以实现动态的负载均衡机制。
1.5.4 消费者负载均衡
Kafka 中的消费者同样需要进行负载均衡来实现多个消费者合理地从对应的 Broker 服务器上接收消息,每个消费者分组包含若干消费者,每条消息都只会发送给分组中的一个消费者,不同的消费者分组消费自己特定的Topic下面的消息,互不干扰。
1.5.5 分区 与 消费者 的关系
Kafka 会为每个 Consumer Group 分配个全局唯一 Group ID,Group 内的 Consumer 共享该 ID,Kafka规定 每个partition信息只能被同组的一个Consumer 消费,在Zk中记录partition 跟 Consumer关系,每个消费者一旦确定了对一个消息分区的消费权力,需要将其Consumer ID 写入到 Zookeeper 对应消息分区的临时节点上。
1.5.6 消息消费进度 Offset 记录
Consumer 对指定消息分区进行消费的过程中,需要定时地将分区消息的消费进度 Offset 记录到 Zookeeper 上,以便在该 Consumer 进行重启或者其他 Consumer 重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息消费。
1.5 7 消费者注册
为让同一个 Topic 下不同分区的消息尽量均衡地被多个 Consumer 消费而进行 Consumer 与消息分区分配的过程。
Consumer 启动后在ZK下创建个节点,并且每个 Consumer 会对 Consumer Group 中的 Consumer 的变化注册监听,目的是为了保证 Consumer 负载均衡。
Consumer 会对Broker列表监听,发生变化会进行 Consumer 负载均衡。
2 Kafka 生成过程
2.1 写入方式
producer采用 push 模式将消息发布到 broker,每条消息都被 append 到 patition 中,属于 顺序写磁盘 ,顺序写比随机写要起码提速3个数量级!
2.2 分区 Partition
2.2.1 Partition 简介
消息发送时都被发送到一个topic,其本质就是一个目录,而topic是由一些 分区日志 Partition Logs 组成,其组织结构如下图所示:
Partition发生
可以看到每个 Partition 中的消息都是有序的,生产的消息被不断追加到 Partition log 上,其中的每一个消息都被赋予了一个唯一的 offset 值。
消费者
通过分区可以 方便在集群中扩展,可以提高并发。
形象理解:
Kafka 的设计源自生活,好比为公路运输,不同的起始点和目的地需要修不同高速公路(主题),高速公路上可以提供多条车道(分区),流量大的公路(主题)多修几条车道(分区)保证畅通,流量小的公路少修几条车道避免浪费。收费站好比消费者,车多的时候多开几个一起收费避免堵在路上,车少的时候开几个让汽车并道就好了。
2.2.2 分区原则
我们需要将producer发送的数据封装成一个ProducerRecord对象。
数据封装
2.3 Kafka 文件存储机制
Kafka存储结构
- 100000000000000000000.index
- 200000000000000000000.log
- 300000000000000170410.index
- 400000000000000170410.log
- 500000000000000239430.index
- 600000000000000239430.log
注意:index 和 log 文件以当前segment的第一条消息的 offset 命名。
数据查找过程
2.4 如何保证消息顺序执行
2.4.1 顺序错乱
Kafka 一个 topic,一个 partition,一个 Consumer,但是 Consumer 内部进行多线程消费,这样数据也会出现顺序错乱问题。
多线程消费
数据有顺序的数据写入到了不同的 partition 里面,不同的消费者去消费,但是每个 Consumer 的执行时间是不固定的,无法保证先读到消息的 Consumer 一定先完成操作,这样就会出现消息并没有按照顺序执行,造成数据顺序错误。
多个消费者
2.4.2 解决办法
确保同一个消息发送到同一个 partition,一个topic,一个partition,一个consumer,内部单线程消费。
单线程消费
在1的基础上,在一个 Consumer 上根据信息ID映射到不同队列,以此加速消费。
内存队列
4 数据可靠性
4.1 消息传递语义
消息传递语义 message delivery semantic ,Kafka 为确保消息在 producer 和 consumer 之间传输。有以下三种传输保障(delivery guarantee):
理想情况下肯定希望系统的消息传递是严格 exactly once,但很难做到。接下来会按照 消息的传播流程大致说下。
4.2 信息从生产者到 Broker
4.2.1 生产者信息发送至Broker
大致步骤如下:
对于Leader回复 ack,Kafka 为用户提供了三种可靠性级别,用户根据对可靠性和延迟的要求进行权衡。
producer不等待 broker 的ack,提供了一个最低的延迟,broker接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据,对应 At Most Once 模式。
但凡没落盘成功信息就丢失了,一般生产不用。
此乃默认值,producer 等待 broker 的 ack,partition 的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据;认为leader返回 信息就成功了。
producer 等待 broker 的 ack,partition 的 leader 和 follower (ISR中的)全部落盘成功后才返回 ack。
但如果在 leader 收到信息返回ok,follower 收到信息但是发送 ack 时 leader 故障,此时生产者会重新给follower 发送个信息。
对应 At Least Once 模式。
4.2.2 如何保证幂等性
如果业务需要数据 Exactly Once,在早期的 Kafka 版本中 只能在下游去重,现在引入了个幂等性,意思就是无论生产者发送多少个重复消息,Server端只会持久化一条数据,
At Least Once + 幂等性 = Exactly Once
启动幂等性,在生产者参数中 enable.idompotence= true,开启幂等性的生产者在初始化时候会被分配一个PID,发送同一个Partition的消息会附带Sequence Number,Broker会对做缓存,以此来判断唯一性。但是如果PID重启就会发生变化,同时不同partition也具有不同的主键,幂等性无法保证跨分区会话的 Exactly Once。
4.3 Kafka Broker 信息落磁盘
数据落盘过程
Kafka Broker 收到信息后,如何落盘是通过 producer.type 来设定的,一般两个值。
sync,默认模式,数据必须最终落盘才算OK。
async,异步模式,数据刷新到OS的 Page Cache就返回,此时如果机器突然出问题,信息就丢失了。
4.4 消费者从 Kafka Broker 消费数据
消费数据
Consumer 是以 Consumer Group 消费者组的方式工作,由一个或者多个消费者组成一个组,共同消费一个topic。每个分区在同一时间只能由group中的一个消费者读取,但是多个group可以同时消费这个partition。如果一个消费者失败了,那么其他的 group 成员会自动负载均衡读取之前失败的消费者读取的分区。Consumer Group 从 Broker 拉取消息来消费主要分为两个阶段:
获得数据,提交 Offset。
开始处理数据。
如果你先提交 offset 再处理数据可能在处理数据时出现异常导致数据丢失。而如果你先处理数据再提交 offset, 如果提交 offset 失败可能导致信息重复消费。
PS:
pull 模式不足之处是,如果 kafka 没有数据,消费者可能会陷入循环中,一直返回空数据。针对这一点,Kafka的消费者在消费数据时会传入一个时长参数 timeout,如果当前没有数据可供消费,consumer会等待一段时间之后再返回,这段时长即为 timeout。
5 Kafka 分区分配策略
同一个 group.id 中的消费者,对于一个 topic 中的多个 partition 中的消息消费,存在着一定的分区分配策略。
在 Kafka 中存在着两种分区分配策略,通过 partition.assignment.strategy 来设置。
5.1 RangeAssignor 范围分区策略
Range 范围分区策略是对每个 topic 而言的。首先对同一个 topic 里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。假如现在有 10 个分区,3 个消费者,排序后的分区将会是p0~p9。消费者排序完之后将会是C1-0、C2-0、C3-0。通过 Partitions数 / Consumer数 来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者将会多消费 1 个分区。
消费者 | 消费的分区 |
---|---|
C1-0 | 消费 p0、1、2、3分区 |
C2-0 | 消费 4、5、6分区 |
C3-0 | 消费 7、8、9分区 |
Range 范围分区的弊端:
如上只是针对 1 个 topic 而言,C1-0 消费者多消费1个分区影响不是很大。如果有 N 多个 topic,那么针对每个 topic,消费者 C1-0 都将多消费 1 个分区,topic越多,C1-0 消费的分区会比其他消费者明显多消费 N 个分区。这就是 Range 范围分区的一个很明显的弊端了.
5.2 RoundRobinAssignor 轮询分区策略
RoundRobin 轮询分区策略是把所有的 partition 和所有的 consumer 都列出来,然后按照 hascode 进行排序,最后通过轮询算法来分配 partition 给到各个消费者。轮询分区分为如下两种情况:
5.2.1 Consumer Group 内 Consumer 订阅信息相同
如果同一消费组内,所有的消费者订阅的消息都是相同的,那么 RoundRobin 策略的分区分配会是均匀的。
例如同一消费者组中,有 3 个消费者C0、C1和C2,都订阅了 2 个主题 t0 和 t1,并且每个主题都有 3 个分区(p0、p1、p2),那么所订阅的所以分区可以标识为t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终分区分配结果如下:
消费者 | 消费的分区 |
---|---|
C0 | 消费 t0p0、t1p0 分区 |
C1 | 消费 t0p1、t1p1 分区 |
C2 | 消费 t0p2、t1p2 分区 |
5.2.1 Consumer Group 内 Consumer 订阅信息不相同
同一消费者组内,所订阅的消息是不相同的,那么分区分配就不是完全的轮询分配,有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个 topic,那么在分配分区的时候,此消费者将不会分配到这个 topic 的任何分区。
例如同一消费者组中有3个消费者C0、C1、C2,他们共订阅了 3 个主题t0、t1、t2,这 3 个主题分别有 1、2、3 个分区(即t0有1个分区(p0),t1有2个分区(p0、p1),t2有3个分区(p0、p1、p2)),即整个消费者所订阅的所有分区可以标识为 t0p0、t1p0、t1p1、t2p0、t2p1、t2p2。然后消费者 C0 订阅的是主题t0,消费者C1订阅的是主题t0和t1,消费者C2订阅的是主题t0、t1和t2,最终分区分配结果如下:
消费者 | 消费的分区 |
---|---|
C0 | 消费 t0p0 分区 |
C1 | 消费 t1p0 分区 |
C2 | 消费 t1p1、 t2p0、 t2p1、 t2p2 分区 |
6 Kafka 高效读写
Kafka 可支持百万 TPS 跟如下几个特性有关。
6.1 顺序读写数据
信息存储在硬盘中,硬盘由很多盘片组成,显微镜观察盘片会看见盘片表面凹凸不平,凸起的地方被磁化代表数字1,凹的地方是没有被磁化代表数字0,因此硬盘可以以二进制来存储表示文字、图片等信息。
磁盘平面图
上图是硬盘的实际图,可能无法理解内部构造,我们来看个形象的图:
磁盘内部图
扇区:硬盘的最小读写单元
块/簇:是操作系统针对硬盘读写的最小单元
page:是内存与操作系统之间操作的最小单元。
一次访盘的读/写请求完成过程由三个动作组成:
可以发现读取主要耗时是在前两个,如果我顺序读取则寻道跟旋转延迟只用一次即可。而如果随机读取呢则可能经历多次寻道跟旋转延迟,两者相差几乎 3个数量级。
随机跟顺序读写在磁盘跟内存中
6.2 Memory Mapped Files 内存映射文件
内存映射
6.3 Zero Copy
6.3.1 直接内存存取 DMA
CPU 发出指令操作 IO 来进行读写操作,大部分情况下其实只是把数据读取到内存,然后从内存传到IO即可,所以数据其实可以不经过CPU的。
Direct Memory Access 的出现就是为批量数据的输入/输出而提速的。DMA 是指外部设备不通过CPU而直接与系统内存交换数据的接口技术。这样数据的传送速度就取决于存储器和外设的工作速度。
如果数据传输的时候只用到了 DMA 传输而没经过 CPU 复制数据,则我们称之为零拷贝 Zero Copy。用了 Zero Copy 技术耗时性能起码减半。
6.3.2 Kafka 读写对比
零拷贝
如上黑色流程是没用 Zero Copy 技术流程:
红色流程是用 Zero Copy 技术流程:
6.4 Batch Deal
消费者拉取数据的时候,Kafka 不是一个一个的来送数据的,而是批量发送来处理的,这样可以节省网络传输,增大系统的TPS,不过也有个缺点就是,我们的数据不是真正的实时处理的,而真正的实时还是要看 Flink。
7 参考
Kafka 为什么要分区 :https://www.zhihu.com/question/28925721
关于磁盘读取:https://blog.csdn.net/holybin/article/details/21175781
Kafka百万TPS:https://mp.weixin.qq.com/s/Fb1cW0oN7xYeb1oI2ixtgQ
本文标题:六问Kafka为啥那么牛!
URL分享:http://www.shufengxianlan.com/qtweb/news15/138465.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联