| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> kafka补充 -> 正文阅读 |
|
[大数据]kafka补充 |
11.LEO、LSO、AR、ISR、HW 都表示什么含义? 讲真,我不认为这是炫技的题目,特别是作为 SRE 来讲,对于一个开源软件的原理以及概念的理解,是非常重要的。
需要注意的是,通常在 12.Kafka 能手动删除消息吗? Kafka 不需要用户手动删除消息。它本身提供了留存策略,能够自动删除过期消息。当然,它是支持手动删除消息的。
13.__consumer_offsets 是做什么用的? 这是一个内部主题,主要用于存储消费者的偏移量,以及消费者的元数据信息 ( 需要注意的是: Kafka 的 14. 分区 Leader 选举策略有几种? 分区的 Leader 副本选举对用户是完全透明的,它是由
这 4 类选举策略的大致思想是类似的,即从 AR 中挑选首个在 ISR 中的副本,作为新 Leader。 15.Kafka 的哪些场景中使用了零拷贝(Zero Copy) 其实这道题对于 SRE 来讲,有点超纲了,不过既然 Zero Copy 是特别容易被问到的高阶题目。在 Kafka 中,体现 Zero Copy 使用场景的地方有两处:基于 mmap 的索引和日志文件读写所用的 TransportLayer 先说第一个。索引都是基于 MappedByteBuffer 的,也就是让用户态和内核态共享内核态的数据缓冲区,此时,数据不需要复制到用户态空间。不过,mmap 虽然避免了不必要的拷贝,但不一定就能保证很高的性能。在不同的操作系统下,mmap 的创建和销毁成本可能是不一样的。很高的创建和销毁开销会抵消 Zero Copy 带来的性能优势。由于这种不确定性,在 Kafka 中,只有索引应用了 mmap,最核心的日志并未使用 mmap 机制。 再说第二个。TransportLayer 是 Kafka 传输层的接口。它的某个实现类使用了 FileChannel 的 transferTo 方法。该方法底层使用 sendfile 实现了 Zero Copy。对 Kafka 而言,如果 I/O 通道使用普通的 PLAINTEXT,那么,Kafka 就可以利用 Zero Copy 特性,直接将页缓存中的数据发送到网卡的 Buffer 中,避免中间的多次拷贝。相反,如果 I/O 通道启用了 SSL,那么,Kafka 便无法利用 Zero Copy 特性了。 深度思考题16.Kafka 为什么不支持读写分离? 这其实是分布式场景下的通用问题,因为我们知道 CAP 理论下,我们只能保证 C (可用性) 和 A (一致性) 取其一,如果支持读写分离,那其实对于一致性的要求可能就会有一定折扣,因为通常的场景下,副本之间都是通过同步来实现副本数据一致的,那同步过程中肯定会有时间的消耗,如果支持了读写分离,就意味着可能的数据不一致,或数据滞后。 Leader/Follower 模型并没有规定 Follower 副本不可以对外提供读服务。很多框架都是允许这么做的,只是 Kafka 最初为了避免不一致性的问题,而采用了让 Leader 统一提供服务的方式。 不过,自 Kafka 2.4 之后,Kafka 提供了有限度的读写分离,也就是说,Follower 副本能够对外提供读服务。 19.Java Consumer 为什么采用单线程来获取消息? 在回答之前,如果先把这句话说出来,一定会加分:Java Consumer 是双线程的设计。一个线程是用户主线程,负责获取消息;另一个线程是心跳线程,负责向 Kafka 汇报消费者存活情况。将心跳单独放入专属的线程,能够有效地规避因消息处理速度慢而被视为下线的 “假死” 情况。 单线程获取消息的设计能够避免阻塞式的消息获取方式。单线程轮询方式容易实现异步非阻塞式,这样便于将消费者扩展成支持实时流处理的操作算子。因为很多实时流处理操作算子都不能是阻塞式的。另外一个可能的好处是,可以简化代码的开发。多线程交互的代码是非常容易出错的。 20. 简述 Follower 副本消息同步的完整流程 首先,Follower 发送 FETCH 请求给 Leader。 接着,Leader 会读取底层日志文件中的消息数据,再更新它内存中的 Follower 副本的 LEO 值,更新为 FETCH 请求中的 fetchOffset 值。 最后,尝试更新分区高水位值。Follower 接收到 FETCH 响应之后,会把消息写入到底层日志,接着更新 LEO 和 HW 值。 Leader 和 Follower 的 HW 值更新时机是不同的,Follower 的 HW 更新永远落后于 Leader 的 HW。这种时间上的错配是造成各种不一致的原因。 因此,对于消费者而言,消费到的消息永远是所有副本中最小的那个 HW。 Kafka的分区数是不是越多越好?分区多的优点kafka使用分区将topic的消息打散到多个分区分布保存在不同的broker上,实现了producer和consumer消息处理的高吞吐量。Kafka的producer和consumer都可以多线程地并行操作,而每个线程处理的是一个分区的数据。因此分区实际上是调优Kafka并行度的最小单元。对于producer而言,它实际上是用多个线程并发地向不同分区所在的broker发起Socket连接同时给这些分区发送消息;而consumer,同一个消费组内的所有consumer线程都被指定topic的某一个分区进行消费。 所以说,如果一个topic分区越多,理论上整个集群所能达到的吞吐量就越大。 分区不是越多越好分区是否越多越好呢?显然也不是,因为每个分区都有自己的开销: 一、客户端/服务器端需要使用的内存就越多 Kafka0.8.2之后,在客户端producer有个参数batch.size,默认是16KB。它会为每个分区缓存消息,一旦满了就打包将消息批量发出。看上去这是个能够提升性能的设计。不过很显然,因为这个参数是分区级别的,如果分区数越多,这部分缓存所需的内存占用也会更多。假设你有10000个分区,按照默认设置,这部分缓存需要占用约157MB的内存。而consumer端呢?我们抛开获取数据所需的内存不说,只说线程的开销。如果还是假设有10000个分区,同时consumer线程数要匹配分区数(大部分情况下是最佳的消费吞吐量配置)的话,那么在consumer client就要创建10000个线程,也需要创建大约10000个Socket去获取分区数据。这里面的线程切换的开销本身已经不容小觑了。 如何确定分区数量呢可以遵循一定的步骤来尝试确定分区数:创建一个只有1个分区的topic,然后测试这个topic的producer吞吐量和consumer吞吐量。假设它们的值分别是Tp和Tc,单位可以是MB/s。然后假设总的目标吞吐量是Tt,那么分区数 = Tt / max(Tp, Tc) 说明:Tp表示producer的吞吐量。测试producer通常是很容易的,因为它的逻辑非常简单,就是直接发送消息到Kafka就好了。Tc表示consumer的吞吐量。测试Tc通常与应用的关系更大, 因为Tc的值取决于你拿到消息之后执行什么操作,因此Tc的测试通常也要麻烦一些。 一条消息如何知道要被发送到哪个分区?按照key值分配默认情况下,Kafka根据传递消息的key来进行分区的分配,即hash(key) % numPartitions:
这保证了相同key的消息一定会被路由到相同的分区。 key为null时,从缓存中取分区id或者随机取一个如果你没有指定key,那么Kafka是如何确定这条消息去往哪个分区的呢?
不指定key时,Kafka几乎就是随机找一个分区发送无key的消息,然后把这个分区号加入到缓存中以备后面直接使用——当然了,Kafka本身也会清空该缓存(默认每10分钟或每次请求topic元数据时)。 Consumer个数与分区数有什么关系?topic下的一个分区只能被同一个consumer group下的一个consumer线程来消费,但反之并不成立,即一个consumer线程可以消费多个分区的数据,比如Kafka提供的ConsoleConsumer,默认就只是一个线程来消费所有分区的数据。
image.png 所以,如果你的分区数是N,那么最好线程数也保持为N,这样通常能够达到最大的吞吐量。超过N的配置只是浪费系统资源,因为多出的线程不会被分配到任何分区。 Consumer消费Partition的分配策略Kafka提供的两种分配策略: range和roundrobin,由参数partition.assignment.strategy指定,默认是range策略。 当以下事件发生时,Kafka 将会进行一次分区分配:
将分区的所有权从一个消费者移到另一个消费者称为重新平衡(rebalance),如何rebalance就涉及到本文提到的分区分配策略。 Range strategyRange策略是对每个主题而言的,首先对同一个主题里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序。在我们的例子里面,排完序的分区将会是0, 1, 2, 3, 4, 5, 6, 7, 8, 9;消费者线程排完序将会是C1-0, C2-0, C2-1。然后将partitions的个数除于消费者线程的总数来决定每个消费者线程消费几个分区。如果除不尽,那么前面几个消费者线程将会多消费一个分区。在我们的例子里面,我们有10个分区,3个消费者线程, 10 / 3 = 3,而且除不尽,那么消费者线程 C1-0 将会多消费一个分区,所以最后分区分配的结果看起来是这样的:
假如我们有11个分区,那么最后分区分配的结果看起来是这样的:
假如我们有2个主题(T1和T2),分别有10个分区,那么最后分区分配的结果看起来是这样的:
可以看出,C1-0 消费者线程比其他消费者线程多消费了2个分区,这就是Range strategy的一个很明显的弊端。 RoundRobin strategy使用RoundRobin策略有两个前提条件必须满足:
所以这里假设前面提到的2个消费者的num.streams = 2。RoundRobin策略的工作原理:将所有主题的分区组成 TopicAndPartition 列表,然后对 TopicAndPartition 列表按照 hashCode 进行排序,看下面的代码应该会明白:
最后按照round-robin风格将分区分别分配给不同的消费者线程。 在这个的例子里面,假如按照 hashCode 排序完的topic-partitions组依次为T1-5, T1-3, T1-0, T1-8, T1-2, T1-1, T1-4, T1-7, T1-6, T1-9,我们的消费者线程排序为C1-0, C1-1, C2-0, C2-1,最后分区分配的结果为:
多个主题的分区分配和单个主题类似。遗憾的是,目前我们还不能自定义分区分配策略,只能通过partition.assignment.strategy参数选择 range 或 roundrobin。 Kafka 的零拷贝技术 1.传统拷贝过程 ? ? ? ?这个过程涉及到 4 次上下文切换以及 4 次数据的复制,并且有两次复制操作是由 CPU 完成。但是这个过程中,数据完全没有进行变化,仅仅是从磁盘复制到网卡缓冲区。? ? ? ? ?在这种情况下,如果能够减少用户空间与内核空间之间的切换,是不是会比传统拷贝快一点呢?如下图: 结果显而易见,毕竟少了 1 次传输过程,肯定会比传统的拷贝性能高。这样子首先数据被从磁盘读取到 Read Buffer 中,然后再发送到 Socket Buffer,最后才发送到网卡。虽然减少了用户空间和内核空间之间的数据交换,但依然存在多次数据复制。 ? ? ? ?明显性能的开销,都消耗在彼此之间的数据复制过程中,那么进一步减少数据的复制过程,或者干脆没有数据复制这一过程,性能会明显增强。这里就需要介绍到 DMA 技术 了 2.DMA 技术的出现 ? ? ? ?传统的内存访问,所有的请求都会发送到 CPU ,然后再由 CPU 来完成相关调度工作。如下图所示: ? ? ? ? 当?DMA 技术的出现,数据文件在各个层之间的传输,则可以直接绕过CPU,使得外围设备可以通过DMA控制器直接访问内存。与此同时,CPU可以继续执行程序。如下图: ?在现代电脑中,很多硬件都是支持 DMA 技术的,这里面其中就包括我们此处用到的网卡。还有其他硬件也都是支持 DMA 技术的,例如:磁盘、显卡、声卡等其他硬件。 3.零拷贝技术 ? ? ? ?现代的 Unix 操作系统提供 了一个优化的代码路径,用于将数据从页缓存直接传输到 Socket; 在 Linux 中,是通过 sendfile 系统调用来完成的。Java 提供了访问这个系统调用的方法:FileChannel.transferTo API 。使用 sendfile ,只需要一次拷贝就行,允许操作系统将数据直接从页缓存发送到网络上。所以在这个优化的路径中, 只有最后一步将数据拷贝到网卡缓存中是需要的。 4.Java零拷贝的实现 ? ? 零拷贝的使用场景一般是: 较大,读写较慢,追求速度 partition 的数据文件 partition 中的每条 Message 包含三个属性: 数据文件分段 segment partition 物理上由多个 segment 文件组成,每个 segment 大小相等,顺序读写。每个 segment数据文件以该段中最小的 offset 命名,文件扩展名为.log。这样在查找指定 offset 的 Message 的时候,用二分查找就可以定位到该 Message 在哪个 segment 数据文件中。 数据文件索引Kafka 为每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为.index。index 文件中并没有为数据文件中的每条 Message 建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。 ? |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 | -2025/3/4 3:25:07- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |