Kafka介绍与高性能原因分析

Kafka是linkedin开源的组织做的一个开源的消息的分布式系统,它目前呢也是属于apache的顶级项目了,也是非常非常的有名气的一款消息中间件。Kafka呢它主要的特点是基于pull的模式来做消息的处理的。也就是说做一个这个拉模式的消息处理。然后呢,他追求高吞吐量,然后整个一开始项目的目的就是为了做日志收集和传输的。

然后呢,对于Kafka它也有很多版本。比如说0.8版本以后,它就开始支持复制,但是呢不支持这个事务哈,因为它追求高性能,它肯定不能去加事务的。加事务毕竟会损耗它的这性能。

对于消息的一些重复消费,丢失,还有错误,他并没有严格的要求。因为他本身来讲就是做收集日志的几百万几千万上亿条,几十亿条数据里丢个两三条,这是没有什么所谓的。它适合在生产大量数据的这个互联网服务中去做业务数据的一个收集。

如果说你想要去做消息一条不丢,Kafka能不能实现呢?答案是肯定的,是可以实现的。但是实现它的性能就会降的比较多了。

所以说呢正常来讲,我们去选择Kafka,一般呢都会容忍它有一些极少量的消息的丢失。

Kafka具有分布式的特性,也就是说它支持消息的一个分区的概念。Kafka里面非常核心的一个概念就是我们的partition
一个topic下面可以有好多个partition。然后呢partition跟我们对应的这个consumer也是一定要一一对应上的。

比如说你一共就四个partition,然后你十几个consumer,其实呢很多consumer就是浪费的哈,没有意义的。这个其实跟我们很多的MQ它的这个初衷都是类似,都是相像的。

然后呢它具有跨平台的特性,它支持不同语言的客户端。比如说我们的java、PHP或者是我们的python都支持。所以说呢它使用起来也是非常非常的友好的,对于一些易购的系统,然后它的实时性是非常强的,它的数据支持这种实时处理和离线处理。即使你的Kafka数据堆积了上亿,几十亿的数据,只要你的这个存储是ok的,它不会影响Kafka的性能。

Kafka的消息堆积的能力也是非常非常强的,绝对是上亿级的消息堆积能力。然后它的这个伸缩性也是非常强的支持这种水平的扩展。ok这个就是我们跟大家来介绍Kafka最关键的四点。

在这里插入图片描述

我们继续往下看Kafka高性能的原因呢,它为什么性能这么高呢?

首先第一点呢就是Kafka的这个顺序写以及Page Catch,Page Catch具有一个特点,就是空中接力,高效读写。

顺序写就是一个顺序写盘,顺序写盘的过程呢,其实呢它可以去提高我们磁盘的这个利用率。你比如说我们的consumer通过offset顺序的去消费这些数据,而不删除已经消费过的数据,从而避免磁盘的一个随机写随机写的一个这一个过程。

让我们回想一下业界主流的一些种种MQ。其实rocket MQ它也是借鉴了Kafka很多很多的特性,在自己业务的基础上去做了一个扩展。如果你要做一个MQ,你怎么去设计这个MQ的存储呢?

很显而易见的就是你要做到顺序写,就是说MQ里的数据,呃,我的生产者把消息发送到了MQ的broker,然后我要存到磁盘的某一个点,我一定要记录一个位置。这个位置就是offset。然后这个offset呢肯定会被我们的consumer去消费。他消费的时候肯定会记录一下他当前消费的位置是在哪儿,就是说他要记录一下他消费的这个offset。然后他基于这个offset之后,再继续去往下去找下一个offset,然后去消费。只有这么一过程,才能去充分的去利用我们磁盘的利用率,因为这才是顺序写一个一个的写,而不是说随机写。

为什么不是随机写呢?如果说你消费完了这个消息,然后你把这个消息从这个文件中的某一点去给它删掉。
你想想它这个整个的文件,它的offset是不是就会变化,它是不是就会产生一个偏移。所以说呢一般MQ的设计都会去不允许删除消息,这是重点。

他这个是怎么去做到的呢?他肯定不是说直接把文件做中的某一个offset的对应的消息删除的,而可能是做了一层转储,然后去打标记去做的。
相对来讲它的删除可能就不是物理删除,而是一些逻辑上的这个让你不可见。

Kafka第一点高效的读写,他其实是利用了这种page cache的这种空中接力的方式,还有用page cache的清理,还有等等很多。
比如说呃后台异步的这个一些flash策略,还有里面有好多scheduler哈。然后主动的flash策略,包括一些预读的策略,IO的调度的一些策略有很多很多。
它的内部其实实现是比较复杂的。

我们来看一看这个page cache空中接力,配置cache这个东西它能够做什么呢?就是说我可以在廉价的这个服务器上,我都能支持单机每秒100K1条哈,100K1条。你后面你自己想加三个零就好。以上的这种吞吐量。所以说Kafka说,你不要害怕文件系统Kafka,它的这个高效读写就是简简单单的去做顺序的去读普通的文件,然后建立于我们linux内核的一个page cache,不显示的去用内存,就是这个概念。

我们继续,我先往下走,高性能高吞吐就是Kafka他的这个初衷了,就是做日志收集的嘛,肯定是具备这个条件的。然后呢,还有哪些高性能的原因呢?刚才我说的它有一些好多好多异步的这种IO级别的这个schedule哈,它能做什么呀?会将连续的小块哈组成一个大块的这个物理的这么一个文件,然后从而提高它的性能。schedule呢可能会尝试的去将一些操作重新按照顺序去排好,然后从而减少磁盘移动的一个时间,然后充分的去利用空闲的一些内存。当然这个空闲内存肯定是非jvm级别内存了,然后呢Kafka,他的读写基本上都是在配置开始进行的。

如果你的生产者跟消费者速度差不多相当,他甚至都不需要通过这个物理级别的这种磁盘去做数据交换,直接就读page cache了。
ok 即使是你重启你自己的这个Kafka,其实page catch 仍然是可用的操作系统级别的这个page cache ,即使你服务器重启,它还是存在的。
但是OS级别的就不行了,你jvm的这个内存,你说你这个tomcat 重启了,肯定都丢了哈,你要重新去加载好了,我们继续往下。
还有就是我们说的这些IO的一些预图策略等等,这就是Kafka一些高性能的原因。

接下来呢我们来看一幅图,这幅图,然后来说一说我们的这个page cache。

首先第一点我们来说一说什么是page cache,也就是页面缓存。
page catch呢是操作系统实现的一种主要的磁盘缓存,在这里要记住是操作系统主要实现的一种磁盘缓存的这种机制或者说策略。
它的目的就是以此来减少对磁盘IO的操作。
因为我们知道对于磁盘IO它访问如果特别频繁,会影响我们整个操作系统的性能。具体来说其实就是把磁盘中的数据缓存到内存中,然后呢,把对磁盘的访问变成为对内存的访问,就是这么一个事情。目前业界很多主流的这个框架或者是架构设计,或者是现代的这个操作系统。它其实为了弥补性能上的差异,它都是直接将内存作为磁盘去做缓存的。

当然这个磁盘缓存要加引号的,甚至说可能会将所有的内存就当做磁盘去用。这样呢也是想要充分的提高我们对于我们操作系统也好,或者是这个某个技术框架也好,就要提高它的这个性能。统一的去对把所有原来对IO的操作都会从这个内存中读取。但我相信很多做过一些高并发的一些项目的一些小伙伴,你都要知道在开始我们最早期的时候,可能大家会读这种关系型数据库,对不对?关系型数据库压力一旦大了怎么办呢?大家可能想到的策略去做这个分库分表。分库分表它可能也有一天会满足不了我们的需求。

你有可能会用一些非光盘数据库,或者是用我们的这个remote cache,比如说远程存储上缓存,像redis,对不对?当然像一些入口级别流量非常非常高,访问量非常非常巨大的一些入口,怎么去扛住了?可能你redis都会扛不住,这个时候可能会借力于我们的内存,对不对?当然我们都知道你把所有的数据都存到java jvm的这个内存中,它也会非常影响我们java的这个程序的性能。

pass cash本质上就是把应该从磁盘中读取的数据转换成从内存中去读取,把对磁盘的访问变成对内存的访问即可。
接下来呢我们来看这幅图,这幅图说的是一个什么事情呢?在这里插入图片描述

这幅图主要就是对于磁盘文件的一个读写操作,映射我们java,比如说在我们的D盘中有一个这个文件叫1.txt, 我们想把它读到我们的这个应用程序里。这个对于OS级别操作系统级别它都做了哪些事情呢?我们可以想一下,其实呢当一个进程准备去读取磁盘上的文件内容的时候,操作系统它首先不是说直接去读这个磁盘文件了,它首先做的事情是什么呢?

他首先肯定会做检查,自己去check,会去将待读取的数据所在的页来看一看这个页在page cache中是否存在。如果存在,就相当于命中了嘛,就直接把数据返回来就可以了,从而避免了什么呢?对物理磁盘的一些个IO操作。但如果没有命中呢,这个时候我们的操作系统会真正的向磁盘发起一次IO请求,并且将读取的数据它不是直接返回给这个进程,它直接呢是先把我们的这个读取出来数据,先加入到缓存页去做一个缓存,然后再把数据返回给进程。他是这么去做的。也可以理解为就是一个空间换时间嘛。这就是一个最简单的文件读写的一个过程。

如果说现在我们想把磁盘文件读到内存里,读到内存里之后,就读到我们这个应用程序里。然后我们通过应用程序再给它写回到或者再写到另外的一个应用程序。这个它经历过哪些过程呢?

首先第一个,大家看到最右侧的这个是我们磁盘文件。他经历第一步骤是什么呢?这是物理磁盘文件,操作系统会把物理的磁盘里的这个内容先写到内核读取的缓冲区,这是操作系统级别的,看叫做内核空间上下文。然后左边的这块儿完全就是我们的这个user connection,就是我们应用程序的上下文,就是你java服务的上下文。

第二步,他做什么事情?他是通过从用户缓冲区去读取操作系统OS级别的这个内核缓冲区的东西。就是说把应用共享程序再次读取一下我们的这个内核空间的这个存储区,把数据再转过来,转到用户的这个缓冲区里。这个时候应用程序里就有了,然后我们再去读什么,这个把它打印一下什么的。在你的这个程序里就能看到这个数据了。假设说我们说哎这个数据我想给他远程的给他传到另外的一个应用程序怎么办?还是这个操作,他现在数据在应用的缓存中了。应用程序的缓存中他肯定会把数据再写入我们操作系统级别的这个OS的这个缓存中去用。然后内核空间缓冲区就是我们操作系统的这个OS缓冲区,然后再把OS缓冲区把数据再传到我们socket的缓冲区。然后socket缓冲区再到达实际的物理网口,然后通过网络传给对应的另一端的消费者。

这个就是我们正常的一个文件读取,然后呢,写给另外一端的一个过程。他会经历过好几次copy,第一次copy是copy到我们的内核中了。第二次copy呢是用户缓存中又copy了一次三次copy,对不对?其实在这里一共有四次copy。

因为我这块这个图已经画出来了,这边文件磁盘文件到我们的内核缓冲区,就是第一次copy。第二次copy呢是我们的用户空间。从我们内核空间去copy了。第三次copy就是用户空间再copy到应用系统,就是操作系统级别的这个内核空间缓存。然后第四次copy是从内核空间的缓存copy到socket缓存区,然后写到网卡。所以他经历过四次的copy,这是我们传统的一个文件读。

其实现在很多很多的应用都是使用什么呀?叫做zero拷贝。其实我们的Kafka,主要就是用zero copy,它节省了这四次copy,它其实就是一次copy。在Kafka中它经常会大量的去使用一些page cache的一些技术,包括zero copy的一些技术,用来提升我们的性能,提升我们的系统的这个吞吐量。

举个例子哈,比如说我们Kafka,可能有一个生产者,有一个消费者,对不对?我们的消费者因为我们Kafka它都是pull的机制,就是拉取的机制。消费者在读取服务端的这个数据的时候,就是他向这个broker去拉取数据的时候,肯定是需要将这个服务端的磁盘文件里的这个数据去读出来,对不对?

比如说磁盘文件,它可能通过网络发送到消费者的进程。然后呢,通常情况下我们的Kafka消息他肯定会有多个这个订阅者生产者发布的消息呢,会被不同的消费者多次消费。为了优化这个流程,Kafka它其实内部就使用了zero copy。

在Kafka中其实大量的使用了缓存页,包括这个copy。这是Kafka实现它这个高性能和高吞吐的一个非常非常至关重要的一个原因。虽然消息都是被写到这个缓存页的,然后呢,我们由操作系统去负责具体的一些这个具体的刷盘策略,刷盘任务。

我们看这幅图就是一个经典的这个zero copy技术了。它跟应用程序是完全没有关联的。我们右边的这个应用程序完全不做任何copy,它就是做什么知道文件直接是在用户内核的空间的上下文做一次copy,对不对?文件描述书,这个叫内核读取缓存区,然后直接写到网卡给消费者。这个过程就是一个zero copy。也就是说我们的zero copy就是零拷贝技术,它只是将我们磁盘文件的数据复制到了页面缓存中一次。然后将数据从页面缓存直接发送到网络中,直接发送到网卡中,就是说发送给不同的这个订阅者的时候,都可以去使用同一个页面缓存,而避免了重复复制的这么一个操作,这个就是一个zero copy。

其实呢通过这个图呢,我们设想一下哈,我如果说现在有十个甚至20个消费者,甚至更多个消费者。现在如果你有十个消费者在传统的数据copy的这种模式下,你至少要经历10乘以40这个次数,为什么呢?因为刚才我们看到这幅图了,是不?如果说你现在有十个消费者,你想把磁盘文件中的数据想发给这十个消费者,是不是要经历40次?

因为你每一次就是四次嘛,而我们的这个copy呢,他需要多少次,同学们想一想,他只需要11次,为什么呀?
第一次他把磁盘文件copy到我们的这个page cache中,然后接下来发十次就好了嘛,是不是发给十个人,是不是把这个page cache中的内容发给十个对应的消费者,对,接到十个网络的这个IP和端口就可以了。

这个就是他提升性能的一一个非常重要的点,也就是说zero copy和page cache。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Escape2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值