IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: 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为什么这么快?

Apache Kafka是一个高性能的消息处理引擎,在众多消息处理产品中,kafka的性能绝对是处于第一梯队的,相信你在工作中或多或少都有使用过,但是你知道kafka为什么这么快吗?接下来,我们就从kafka的设计的多个维度来探秘一下,kafka的一些"独门绝技"?

批量消息

客户端虽然是逐条发送消息,但是kafka的producer会把发送的数据先"攒一波",作为一个"批消息"发送给broker。而当broker收到这批消息后,并不会把这个批消息拆解开逐个进行处理,而是作为一条批消息进行处理,在broker中,无论是把消息写入磁盘,从磁盘中读出来,还是在把消息复制到其他follower分区副本上,都是以批消息作为单位的。而消费端消费的时候,也是读取一批消息,并在消费端对着一批消息进行拆解,然后逐个处理。

也就是说,消息在kafka服务端一直都是批消息,批消息的组装和拆解是在客户端(发送端和消费端)进行的,这样不仅减轻了broker端的压力,而且减少了broker处理请求次数,提升了kafka总体的消息处理效率。

批消息发送流程示意图:
在这里插入图片描述


不过攒一波发送消息的机制,会带来一定的消息延迟。就是说,我们使用producer发送了一条消息,但是这条消息并不会立即发送到broker中,而是需要满足一定的"条件",才会发送。如果没有满足这些条件的话,producer是不会发送到broker中,自然在consumer也不会收到这条消息,对于consumer来说,就是没有发送过消息一样。

上面说的条件,主要有两个:
1.发送给某个分区,积压的消息数据占用的空间达到一定规模后,统一发送,这个规模由参数 batch.size决定,参数默认值16kb。

2.producer调用send方法发送消息时,消息会延时发送,延时的最大时间间隔由参数 linger.ms 决定,改参数的默认大小时0ms,也就是立即发送。

在发送消息时,只要满足上面任何一个条件,消息才会被发送出去。其实这也是架构设计上的一种trade off:通过牺牲实时性,来提高吞吐率。

顺序读写

对于磁盘来说,它有一个特性:顺序读写的性能要远远好于随机读写,尤其是机械硬盘。因为每次随机访问前,硬盘都要先进行寻址操作,也就是找到需要访问数据的起始位置,然后从这个位置开始读取数据,而对于机械硬盘的,这个寻址操作是比较消耗性能的。

而顺序读写可以省去大部分的寻址时间,它只需要寻址一次,就可以连写读写下去。所以顺序读写相比随机读写性能要好很多。除此之外,在计算机领域有一个著名的原理,叫做"局部性原理",局部性原理分为:空间局部性和时间局部性。如果一个内存位置被重复的引用,那就是有了时间局部性,如果一个内存位置被引用了,很快这个位置的附近位置也被引用了,这就有了空间局部性,

kafka就是充分利用了磁盘的这个特性。在kafka中,每个分区的数据都对应一个log文件,对于这个分区的数据,都会顺序的写入到这个log文件,当这个文件写满之后,就会开启一个新的文件继续顺序写下去。而读取某个分区中的消息,也是从指定的log文件中,顺序的读取。

不过,细心的老铁,应该会发现其中的问题:因为每个分区的数据,都有自己独立的log文件存储,读取这个分区的数据可以使用顺序读,但是如果分区比较多的话,读取多个分区中的数据,不也是会产生随机读写吗?是的,对于这种情况,kafka就无能为力了,所以在使用kafka的时候,kafka社区会建议,在一个kafka集群中,不要创建过多topic和分区。

而对于这种情况,国内的RocketMQ可以很好的解决这个问题,因为在RocketMQ中,消息的存储是以Broker为单位的,也就是一个Broke上的的所有分区的数据都写入到一个log中,这样可以减少一定的随机读写。如果一个Broker上有上千个主题分区时,RocketMQ的读写性能就会略好于Kafka。

PageCache

PageCache是操作系统在内存中给磁盘文件建立的缓存,是系统级别的缓存,和语言无关。当写数据的时候,数据会先写到PageCache中,操作系统会定时的将PageCach中的数据写到磁盘中,这种攒一波的操作,主要就是为了减少和磁盘IO交互的次数。

读数据的时候,也是先会从PageCache中的查询,如果PageCache中没有要查询的数据时,操作系统会触发一个缺页中断,中断的响应程序会从磁盘中读取数据,加载到PageCache中,然后在返回给客户端。

PageCache作为一种缓存,会占用内存空间,而内存空间是有限的,当空间不足的时候,需要进行内存置换,置换的策略一般是LRU或者是其的变种算法,就是优先保留最近一段时间最常用的那些PageCache。

PageCache工作原理如下图:
在这里插入图片描述

ZeroCopy

对于所有的消息队列服务端程序来说,做的大部分事情就是通过网络来接受数据,然后,为了保证消息的可靠性,会将消息存储到磁盘中,最后再从磁盘中读取数据,通过网络将消息发送出去。简单来说,消息队列的绝大部分工作都是在做磁盘和网络之间的数据交互。

而将磁盘中的数据通过网络发送出去的过程大概如下:
在这里插入图片描述
1.cpu在用户态,调用read函数(系统调用)。

2.cpu从用户态切换到内核态,通过磁盘驱动程序,将磁盘中的文件内容copy到内核缓冲区(read buffer),这个copy过程无需cpu参与,然后将内核缓冲区中数据copy到用户缓冲区,此copy需要cpu参与。

3.cpu从内核态切换到用户态,将用户缓冲区中数据copy到内核缓冲区(socket buffer),此过程需要cpu参与,然后cpu在切换为用户态,处理其他用户程序。

4.socket中的数据通过网卡驱动程序,经过网卡发送出去。

在这个过程中,需要cpu进行3次状态转换,也就是我们常说的上下文切换。同时数据需要先从内核缓冲区复制到用户缓冲区,然后在从用户缓冲区复制到内核缓冲区,而且这两个过程需要cpu的参与。可看出将磁盘中的数据发送出去,是一个比较重的IO操作,也是比较消耗性能的地方,如果能减少这个过程cpu的上下文切换的次数和数据在多个缓冲区间复制的次数,可以提高每次将磁盘数据通过网络交互的效率,而消息队里每天要处理的数据是海量,总体来说,提升的效率也是很客观的。

而Zero Copy技术,可以对上述过程进行优化,来减少此过程中,cpu的参与和上下文切换。

不过这里要先说明一下,Zero Copy和Page Cache相似,都是操作系统提供的一项能力,和具体语言无关,操作系统通过系统调用的方式,对上层的编程语言暴露接口。例如java语言中,NIO的
FileChannel.transferTo(long position, long count, WriteableByteChannel target)
方法的实现就是使用操作系统提供的
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
来实现的。

说了这么多,使用的Zero Copy后,上述过程会变成什么样呢?
在这里插入图片描述

1.cpu在用户态,调用read函数(系统调用)。
2.cpu从用户态切换到内核态,通过磁盘驱动程序,将磁盘中的文件内容copy到内核缓冲区(read buffer),这个copy过程无需cpu参与。
3.网卡程序直接从read buffer中读取数据通过网卡发送出去。

这里把上下文的切换次数从4次减少到2次,同时也把cpu的参与的copy的次数降低到了0。

总结

其实以上这些技术和原理,并不是kafka特有的,只不过kafka把他们充分利用了起来,而且使用的很好,可以作为这些技术的最佳实践了。那么我们自己的在工作中,也可以利用这里技术和特性,具体这么用,kafka的源码就是最好的例子。

在这里小编突然有一个感悟,现在很多第三方框架越来也多,每个框架都有自己的亮点,在某些方面性能很高,使用起来很方便等。但是稍微了解一下这个框架或者工作的特性原理后,就会发现:使用的计算机领域的原理和知识基本上都是在上学期间学习过的。知识还是那些知识,只不过用的姿势发生了变化。所以,与其学各种各样的框架,不如把基础知识学好,这样无论学习何种框架我们都可以明白它的大致工作原理,然后学习一下,这些框架是如何在细节上实践这些原理的,这样学习起来也会变得很轻松。

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-08-23 16:45:39  更:2021-08-23 16:46:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:30:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码