Kafka效率篇-提升效率三板斧

kafka在效率上做了很多的努力。最初的一个使用场景是处理网页上活跃的数据,它往往有非常大的体量,每个页面都能产生数十条写入。而且我们假设每条消息都会被至少一个消费者消费(通常是多个),因此,我们努力让消费变得更加的简单。

我们发现,从构建和运行一定数量的类似的系统的经验来看,消费的效率是有效的多租户运营的关键。假如下游基础设施服务由于应用系统的略微使用就会很容易达到瓶颈,那么很小的变动通常会产生问题。通过效率变得更高,我们可以确保应用系统在相当的负载下,会比下游基础设施更早出问题。这句话的意思就是,kafka系统作为基础设施,会比应用系统更加能抗压力。当你尝试运行一个能够容纳数十或成百应用的中心化集群服务,这是非常重要的一点,因为由于使用模式的改变几乎每天都在发生。

上一节讨论了磁盘效率的问题,假如磁盘访问效率低的问题已经被消除,那么在这类系统中还有两个常见的低效问题

  • 许多小型的I/O操作
  • 过多的字节拷贝

低效问题解决

小型的IO操作

先来说小型的IO操作,这类问题发生在客户端和服务端(broker)之间,以及服务端自己的持久化操作。为了避免这个问题,我们的协议是去构建一个抽象的“数据集”的概念,用来组织一批消息。这个操作允许将消息放在一起发送网络请求并且分摊网络往返的开销,而不是说同一时间只发送一条消息。这带来的好处就是:broker端也能够一次性将一块块的消息附加到日志文件中去,而消费者端也能够同时拉取一大块连续的消息。
单条-批量发送

上图简单说明了,通过消息集的方式发送,能够大幅度减少网络请求,增加kafka的吞吐。

但针对单条消息来说,转微批的方式,会产生一定的延时。直接影响的两个参数为:linger.ms + batch.size。

这两个参数,这里有个误区,我先解释下这两个参数分别的作用:

  • linger.ms:它是kafka 生产者的一个配置,用来控制批量发送消息的等待时间,就是说,当我收到一条消息后,我会默认等待linger.ms的时间,才会发送这批消息,默认linger.ms这个参数默认值是0。
  • batch.size:这个参数可能熟悉的人比较多,它也是用来控制批量发送消息的,不过linger.ms控制的是时间,而batch.size控制的是消息的大小,一旦在producer端暂存的消息大小超过了batch.size,那么就会执行send动作,这里的批是按照partition分组的,就是说发往每个partition的大小不能超过batch.size的大小,否则就会直接发送。默认值为:16384byte,即16KB。

问题来了,很多人在kafka调优过程中,会配置batch.size的大小,比如说调整到1MB或2MB。但是此时kafka吞吐并没有变化,这是为什么呢?

其实问题就出现在linger.ms。消息转微批发送这个动作,是由linger.ms和batch.size两个参数来控制的,即只要这两个参数满足一个,就会将这一批消息发送出去。而linger.ms的默认值为0,就意味着,只要来一条消息,就会立马发送,此时batch.size是不会生效的。

题主在压测过程中,将linger.ms调整成100ms,此时整体业务的tps由200提升到350,说明微批生效。(数字不具备参考意义)

因此:想提高一批消息的大小,要同时调整linger.ms和batch.size。

转批式发送之后,发送的网络包更大了,针对磁盘的操作也有更大的磁盘顺序操作,在内存中也是更大的连续内存块了。这一个操作,将上游随机的消息写入转成了顺序写入。

字节拷贝

  另一个低效的操作是字节拷贝。当消息接受的速率较低时,这不会是一个问题,但是假如负载上来之后,影响就会很大。为了避免这个问题,我们在producer、broker、comsumer端都使用了标准的字节消息格式,这意味着在消息传输过程中,不需要做修改。

在broker端,消息日志就是以文件目录的形式保存的,每个文件由一系列的消息集填充,这些消息集以同样的格式被生产者和消费者使用。保持相同的格式允许一个最重要的优化手段:持久日志块的网络传输。 现代linux操作系统提供了一个高度优化的代码路径,用于将数据从页缓存传输到socket。在操作系统中,这个操作是由sendfile这个系统调用完成的。

在理解sendfile之前,我们先了解一下一般的数据是怎么从文件传输到socket:

  1. 操作系统从磁盘中读取数据并写入到内核空间的页缓存中去
  2. 应用系统将数据从内核区读取到用户空间下的缓冲区
  3. 应用系统将修改后的数据写回到内核空间,并写入到socket缓冲区。
  4. 操作系统将数据从socket 缓冲区拷贝到NIC 缓冲区并发送到网络

下图解释了传统数据的拷贝流程:

在这里插入图片描述

下图描述了文件传输到socket产生的上下文切换:

在这里插入图片描述

以上能看出来明显的低效,其中涉及到四次数据拷贝以及两次系统调用。而使用sendfile,避免了重复的拷贝动作,并且允许操作系统直接将数据从页缓存发送到网络中去,在这个优化下,只会存在一次CPU级别的拷贝动作(页缓存-NIC缓冲区)以及一次系统调用(sendfile)。

先粗略介绍一下零拷贝的使用,可以通过调用transferTo()方法,其底层就是使用了sendfile。

public void transferTo(long position, long count, WritableByteChannel target);

下图表示了使用sendfile之后,我们发送文件需要的代价:

在这里插入图片描述

细节内容,建议大家看官网的解释:

https://developer.ibm.com/articles/j-zerocopy/

我们期望的用户使用场景为一个topic多个消费者,这种场景下,使用了零拷贝之后,数据只需要被拷贝到页缓存,并且就能够被每个消费者重复使用了,而不是将数据保存在内存中并且每次读取都需要拷贝到用户空间去。这就允许消息的消费速率几乎能够达到网络的速率,也就是说此时网络带宽是我们消息系统的瓶颈(压测调优过程中,往往也是以带宽打满为标准)。

页缓存和sendfile的组合意味着在消费者连接的kafka集群中,你看不到磁盘的读取动作,数据几乎都会存在于缓存中。当然异常场景下也会有一些性能问题,这里我举个栗子:

  • 假如测试利用脚本启动了大量的消费者,并且消费大量无关的topic,导致了broker所在的机器上,cache被大量的占用了,此时,真正有效的topic数据,可能会由于内存淘汰策略,已经被刷入到磁盘中去了,这就导致了真正需要用到的topic的数据,每次消费,都需要从磁盘中读取了。

以上是一个真实的案例。

TLS/SSl操作只能在用户空间,目前kafka还不支持内核态的SSL_sendfile。由于这个限制,假如开启了SSL,那么sendfile就会失效。

可以通过以下两个配置开启SSL:

  • security.protocol
  • security.inter.broker.protocol

端到端批量压缩机制

上面有提到,在压测过程中,往往瓶颈不是cpu或内存,而是带宽。这个对于需要在两个数据中心之间传输的场景尤为明显。当然用户可以一个一个的压缩待发送的消息,但是这就造成很低的压缩比。

因为大部分数据的冗余,都是因为重复性,可能由于相同的类型或者json报文中相同的key。有效的压缩操作,最好需要压缩大量的消息而不是单条单条的压缩。

Kafka支持高效的批处理格式,一批消息可以被归在一起,压缩然后发送给服务端(broker)。服务端为了校验这批消息,会解压这批数据。例如说会校验这批消息的数量是否与请求头中消息数保持一致(验证消息是否丢失)。这批消息会以压缩的格式保存在服务端,在服务器日志中会保留压缩的形式并且以同样的形式发送到消费者端。因此消费者端需要以相同的解压缩协议对消息进行解压。

目前kafka支持的压缩协议有:GZIP、Snappy、LZ4和ZStandard。其中GZIP是比较推荐的。

总结

总结一下本文的大致内容,主要解释了kafka如何提高效率的,主要解决了小型IO和大量的字节拷贝问题。

  • 小型IO的问题,kafka的解法是转微批的方式。
  • 字节拷贝问题,kafka利用了零拷贝技术实现,减少了数据的重复拷贝问题,但目前还没做到真正的“零拷贝”。
  • 利用压缩技术,使的网络带宽能够更高效的使用。
  • 后续会出一个关于零拷贝的文章(先埋个坑)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/690191.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2 GPIO控制

ESP32的GPIO的模式,一共有输入和输出模式两类。其中输入模式:上拉输入、下拉输入、浮空输入、模拟输入;输出模式:输出模式、开漏输出(跟stm32八种输入输出模式有所不同)。库函数中控制引脚的函数如下&#…

如何将Hyper-V转VMware?反之亦可

为何要在Hyper-V和VMware之间进行转换呢? 尽管VMware和Microsoft Hyper-V都是当前流行的一类虚拟机监控程序,但它们并不相互兼容。VMware产品使用VMDK格式创建虚拟磁盘,而Hyper-V则使用VHD或VHDX格式创建虚拟磁盘。 有时您可能需要进行这种转…

解读丰田生产方式的工作原理

丰田生产方式,被誉为现代制造业的瑰宝,以其独特的精益生产理念,引领了全球制造业的变革。本文将从丰田生产方式的核心理念、两大支柱、应用实践以及面临的挑战等方面,全面解读其工作原理。 丰田生产方式,又称精益生产&…

弱监督语义分割-对CAM的生成过程进行改进1

一、仿射变换图像结合正则项优化CAM生成 论文:Self-supervised Equivariant Attention Mechanism for Weakly Supervised Semantic Segmentation (CVPR,2020) 1.SEAM方法 孪生网络架构(Siamese Network Architecture&#xff09…

圆柱齿轮的旋向如何判断?

上期出了个题,给了两个内齿轮,请大家来判断他们的旋向,看到了有不少小伙伴评论给出了自己的答案,正确和错误差不多各半吧,错的占比要大一些。这期咱们就好好聊一聊这个问题。 外齿轮的旋向大家貌似判断都没什么问题&a…

数据的均匀化分割算法(网格划分法、四叉树法(含C++代码))

数据的均匀化分割主要是指在分割过程中尽可能均匀地将数据点分布在各个子区域中,以保持数据分布的平衡和优化数据结构的性能。以下是几种可以实现数据均匀化分割的方法: 一. 网格划分法 1. 基本概念 虽然传统的网格划分法不是动态调整的,但通…

Android AOSP Ubuntu源码编译电脑卡顿问题定位解决

文章目录 问题概述分析问题解决问题查看交换分区创建交换分区删除交换分区调整交换分区的活跃度 问题概述 开发SystemUI时,使用内存为16G的主机,Ubuntu 20.04的系统编译SystemUI的源码,编译的过程中发现电脑卡顿,鼠标不能移动。必…

vue2 八大组件通信,父子通信,跨层级通信,事件总线,vuex等

文章目录 什么是组件通信?父子通信流程propsProps 定义Props 作用特点数组写法对象写法(props校验)简写只验证数据类型:完整写法,完整的验证: props父向子传值用props父传子在子组件中修改props $emit子向父…

scrapy的入门

今天我们先学习一下scrapy的入门,Scrapy是一个快速的高层次的网页爬取和网页抓取框架,用于爬取网站并从页面中提取结构化的数据。 1. scrapy的概念和流程 1.1 scrapy的概念 我们先来了解一下scrapy的概念,什么是scrapy: Scrapy是一个Python编写的开源网络爬虫框架…

【VTKExamples::Rendering】第四期 相机插值(CameraInterpolate)

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享VTK样例CameraInterpolate,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. CameraInterpol…

开源基金会AtomGit教程

大家伙,我是雄雄,欢迎关注微信公众号:雄雄的小课堂。 注册 点击链接进入官网,先注册,然后登录:https://atomgit.com/ 编辑个人资料,修改一下自己的信息,用户名后面提交代码的时候要…

程序环境和预处理、编译链接过程、编译的几个阶段、运行环境、预定义符号等的介绍

文章目录 前言一、程序的翻译环境和执行环境二、编译链接过程三、编译的几个阶段四、运行环境五、预定义符号总结 前言 程序环境和预处理、编译链接过程、编译的几个阶段、运行环境、预定义符号的介绍。 一、程序的翻译环境和执行环境 在 ANSI C 的任何一种实现中&#xff0c…