消息队列 CKafka 跨洋数据同步性能优化

导语

本文主要介绍了 CKafka 在跨洋场景中遇到的一个地域间数据同步延时大的问题,跨地域延时问题比较典型,所以详细记录下来做个总结。

一. 背景

为了满足客户跨地域容灾、冷备的诉求,消息队列 CKafka 通过连接器功能,提供了跨地域数据同步的能力,支持跨地域秒级准实时数据同步。

整体的架构图:

如上图所示,CKafka 跨地域数据同步能力,底层基于 Kafka Connect 集群实现,并通过Vpcgw Privatelink 打通云上环境。

数据同步主要流程如下:

1.  Connect 集群初始化 Connect Task,每个 Task 会新建多个 Worker ConsumerClient(具体多少取决于源实例的分区数)从源 CKafka 实例拉取数据。

2.  Connect 集群从源实例拉取数据后,会启动 Producer 发送数据到目标 CKafka 实例。

在某个客户业务场景中,客户希望通过跨地域同步能力,把香港 CKafka 实例的数据同步到美东 CKafka 实例,在使用过程中引发了一个跨地域延时的诡异问题!

二. 问题现象

客户在使用跨地域同步能力的时候,发现数据从香港->美东同步数据的延时非常大,并且能明显的看到 Connect 作为 Consumer 去源实例(香港)消费拉取数据的消息堆积非常大。

消息堆积

根据过往的经验,我们国内地域的同步不会出现这么大的延时,为什么这次的跨地域会有这么大的延时呢?

三. 问题分析

消息堆积常见的原因

Kafka 在生产消费过程中,出现消息堆积常见的原因主要有以下几点:

● Broker 集群负载过高:包括 CPU 高、内存高、磁盘 IO 高导致消费吞吐慢。

● 消费者处理能力不足:如果消费者的处理能力不足,无法及时消费消息,就会导致消息堆积。可以通过增加消费者的数量或者优化消费者的处理逻辑来解决该问题。

● 消费者异常退出:如果消费者异常退出,就会导致消息无法及时消费,从而在 Broker 中积累大量未消费的消息。可以通过监控消费者的状态和健康状况,及时发现并处理异常情况。

● 消费者提交偏移量失败:如果消费者提交偏移量失败,就会导致消息重复消费或者消息丢失,从而在 Broker 中积累大量未消费的消息。可以通过优化消费者的偏移量提交逻辑,或者使用 Kafka 的事务机制来保证偏移量的原子性和一致性。

● 网络故障或者 Broker 故障:如果网络故障或者 Broker 故障,就会导致消息无法及时传输或者存储,从而在 Broker 中积累大量未消费的消息。可以通过优化网络的稳定性和可靠性,或者增加 Broker 的数量和容错能力来解决该问题。

● 生产者发送消息速度过快:如果生产者发送消息速度过快,超过了消费者的处理能力,就会导致消息堆积。可以通过调整生产者的发送速度,或者增加消费者的数量来解决该问题。

基于以上原因,我们首先排查了 Connect 集群所有节点和源目标 CKafka 实例所有节点的负载,发现各项监控指标都很健康、集群负载很低,ConnectConsumer 消费能力也没有出现异常和性能瓶颈。

但是单次拉取消息的速率却很低,平均消费速度325KB/s,这个是不符合预期的。

(注:上图中的 bytes-consumed-rate 指标代表每秒消费的字节数)

既然集群负载没有问题,于是我们进行了更深层的排查分析:

第一阶段分析:排查网络速率

消息延时大,我们首先想到的就是网络问题,所以立刻着手压测网络。通过 Iperf3 、Wget 探测网络速率。

Iperf3 压测,速度在225Mbps 。

Wget 在 Connect 集群直连香港,下载速度在20MB/s。

这两个测试说明:在同样环境下,网络传输速率并不低,可以达到20MB/s。那既然网络带宽没问题,问题又会出在哪呢?

第二阶段分析:内核调参数

网络没有问题,那会不会是 Kafka 网络相关的应用程序参数、以及内核网络相关的参数设置的不合理呢?

1、我们首先进行了内核调参,跟网络相关的内核参数主要有:

系统默认值:
net.core.rmem_max=212992
net.core.wmem_max=212992
net.core.rmem_default=212992
net.core.wmem_default=212992
net.ipv4.tcp_rmem="4096    87380   67108864"
net.ipv4.tcp_wmem="4096    65536   67108864"---------------------------------------------------------
调整内核参数:
sysctl -w net.core.rmem_max=51200000
sysctl -w net.core.wmem_max=51200000
sysctl -w net.core.rmem_default=2097152
sysctl -w net.core.wmem_default=2097152
sysctl -w net.ipv4.tcp_rmem="40960 873800 671088640"
sysctl -w net.ipv4.tcp_wmem="40960 655360 671088640"调整TCP的拥塞算法为bbr:
sysctl -w net.ipv4.tcp_congestion_control=bbr

整体内核参数的值我们都调大了(尽管我们认为系统内核默认值也不小),同时我们还调整了TCP 的拥塞算法。

这里解释一下为什么要调整 TCP 的拥塞算法。

(参考资料:[[译] [论文] BBR:基于拥塞(而非丢包)的拥塞控制(ACM, 2017)]([译] [论文] BBR:基于拥塞(而非丢包)的拥塞控制(ACM, 2017)))

因为这个延时发生在跨地域间且跨洋了,使用 BBR,可以获得显著的网络吞吐量的提升和延迟的降低。吞吐量的改善在远距离路径上尤为明显,比如跨太平洋的文件或者大数据的传输,尤其是在有轻微丢包的网络条件下。延迟的改善主要体现在最后一公里的路径上,而这一路径经常受到缓冲膨胀(Bufferbloat)的影响。所谓“缓冲膨胀”指的是网络设备或者系统不必要地设计了过大的缓冲区。当网络链路拥塞时,就会发生缓冲膨胀,从而导致数据包在这些超大缓冲区中长时间排队。在先进先出队列系统中,过大的缓冲区会导致更长的队列和更高的延迟,并且不会提高网络吞吐量。由于 BBR 并不会试图填满缓冲区,所以在避免缓冲区膨胀方面往往会有更好的表现。

经过内核参数调整后,验证发现延时并没有很大的改善。

2、在云产品技术服务专家大佬的提醒下,确认连接的 Receive Buffer 设置过小,调内核参数才没有生效,怀疑是应用层进行了设置。

于是我们调整了 Kafka 应用程序网络参数 Socket.Send.Buffer、Socket.Recevie.Buffer 的参数值:

(1)把源目标 CKafka 实例 Broker 的 Socket.Send.Buffer.Bytes 参数从默认64KB调整为使用系统的 Socket Send Buffer。

Kafka 内核关于 Socket Send Buffer 的代码:

Tips】:

在 Kafka 中,TCP 发送缓冲区的大小由应用程序和操作系统共同决定。应用程序可以通过设置 Socket.Send.Buffer.Bytes 参数来控制 TCP 发送缓冲区的大小,操作系统也可以通过设置 TCP/IP 协议栈的参数来控制 TCP 发送缓冲区的大小。

应用程序设置的 Socket.Send.Buffer.Bytes 参数会影响 TCP 发送缓冲区的大小,但是操作系统也会对 TCP 发送缓冲区的大小进行限制。如果应用程序设置的 Socket.Send.Buffer.Bytes 参数超过了操作系统的限制,那么 TCP 发送缓冲区的大小就会被限制在操作系统的限制范围内。如果应用程序设置的 Socket.Send.Buffer.Bytes=-1 ,那么 TCP 发送缓冲区的大小就会默认使用操作系统的 TCP 发送缓冲区的大小。需要注意的是,TCP 发送缓冲区的大小会影响网络的吞吐量和延迟时间。如果 TCP 发送缓冲区的大小过小,会导致网络的吞吐量和性能下降;如果 TCP 发送缓冲区的大小过大,会导致网络的延迟时间增加。因此,需要根据实际情况进行调整,以达到最优的性能和可靠性。

(2)把客户端 Connect Consumer 的 Receive.Buffer.Bytes 参数从默认64KB调整为使用系统的Socket Receive Buffer。把客户端 Max.Partition.Fetch.Bytes 这个分区最大拉取大小调整到了5MB。

调整后,我们迅速和客户协调时间重启集群,验证这个调参。调整完后的效果明显:单连接的平均速度从300KB/s提升到了2MB/s以上:

可以看到调大 Kafka 的 Socket 接收、发送参数后,效果确实很明显,同步数率上来了。当我们以为延时问题解决了的时候,问题又出现了!

第三阶段分析:深挖根因

上面第二阶段的 Kafka 调参应用到客户集群,观察一天后,客户反馈集群整体延时有所好转,但是部分分区延时还是很大。我们也观测到大概一半分区的同步速率依然很低。

(注:上图中的 Bytes-Consumed-Rate 指标 代表每秒消费的字节数)

(1)为什么部分连接速度还是很低?

我们首先通过运营后台确定了消费速率低的 Partition 对应的 ConsumerGroupID,通过这个ConsumerGroupID 抓包定位对应的慢速 TCP 连接。

定位连接后,进行抓包分析:

从上可以看到 Server 发送一段数据之后,会暂停一段时间,大约一个 RTT 再继续发送。统计每个发送间隔之间的数据包的总大小,大概64KB。这基本能说明 TCP 的发送窗口被限制在64KB。但是,通过抓包其他速度正常的连接发现并没有这种限制。一般来说,TCP 发送窗口的实际大小是跟 Window Scale 有关的,这个只能在连接建立的时候确认。

Tips】:TCP Window Scale, TCP 的窗口缩放因子。(参考资料:How to determine TCP initial window size and scaling option? Which factors affect the determination? - Red Hat Customer Portal)

在传统的 TCP 协议中,TCP 窗口大小的最大值只能达到 64KB,这限制了 TCP 协议的传输速度和效率。为了解决这个问题,TCP Window Scale 机制被引入到 TCP 协议中。

TCP 窗口大小 = (接收端窗口大小) * (2 ^ TCP Window Scale 选项的值)

需要注意的是,TCP Window Scale 机制需要在 TCP 三次握手连接建立时进行协商,以确定 TCP 窗口大小的扩展方式和参数。

为了抓取建连的情况,我们尝试重启单个 Partition 的消费任务,但是发现,只要一重启,消费的速度就能恢复,窗口的大小就不会出现瓶颈。

(2)为什么发送窗口被限制?

为了复现问题,我们模拟构造了客户的使用场景,进行了整体的场景复现。最终确认只有在任务全量重启的时候才会出现这个问题。 在任务重启过程中,我们进行了服务端的整体抓包。定位到了正常连接和异常连接,对比了建连的过程,最终确认了慢速的连接中 Window Scale 确实没有生效!

正常连接建连过程:

慢速连接建连过程:

从上图可以看出,慢速连接中,Server 在返回 Syn/Ack 包的时候,没有"WS=2",说明并没有开启 Window Scale 选项,进而导致整个连接的发送窗口被限制在了64KB,吞吐就上不去了。Client 返回最后一个 Ack 的时候,也明确显示了"no window scaling used"。

(3)为什么Window Scale 概率性不生效?

到这一步,我们就需要解释为什么 Server 端发送 Syn/Ack 的时候会概率性地不开 Window Scale 呢? 这里,计算组大佬给出了一个相似的 Case 提供我们学习:kubernetes - 深度复盘-重启 etcd 引发的异常 - 个人文章 - SegmentFault 思否 从中可以得到一个信息: 看起来是 SYN Cookie 生效的情况下,对方没有传递 Timestamp 选项过来(实际上,按照 SYN Cookie 的原理,发送给对端的回包中,会保存有编码进 Tsval 字段低 6 位的选项信息),就会调用 Tcp_Clear_Options, 清空窗口放大因子等选项。 我们从系统日志里面,我们也能观察到在任务整体重启的时候确实触发了 SYN Flood。

(4)为什么服务端没收到 Tsval (TCP 的 Timestamp Value) 呢?

上面有介绍过我们的数据同步时经过了公司内部的一个 VPCGW,我们分别在 Client 和 Server 上分别抓包,最终确认是 VPCGW 把 Client 发出的 Tsval 吞了。同时也跟 VPCGW 的研发同学确认,在 NAT 环境下,不转发 Timestamp 是预期行为,主要为了解决特殊情况下的丢包问题,NAT环境下tcp_timestamps问题_centos nat tcp_timestamps_清风徐来918的博客-CSDN博客。不过这个问题在新内核中,已经不存在,所以可以排期提供开放 Timestamp 的能力。

根因定位

一路分析深挖下来,至此,问题的根因就清晰了:

Connect Consumer 批量启动,触发了大量新建 TCP 连接,短时间的大量新建连接触发了 SYN Cookie 保护检查逻辑。但是因为客户端没有发送 Timestamp 选项传过来,造成了服务端把窗口放大因子清除,最终造成连接的发送窗口最大64KB,在大延迟的场景下影响了传输性能。

四. 我们的解决方案

问题的根因找到了,解决方案就清晰明朗了。

● 规避方案:我们调整了 Connect Woker 初始化的并发度,降低 TCP 初始化建连的速度,保证不会触发 SYN Cookie, 来保证后续数据同步的性能。

● 最终方案:推动 VPCGW 打开 TCP Timestamp 的能力。

五. 总结

问题表面是跨地域数据同步请求慢的问题,但是一路深挖下来确实一个非常底层的网络问题。

这个问题的发生比较罕见,因为这个问题发生的条件比较复杂,主要是跨地域存在网络延时、同时大量的 TCP 建连、加上 VPCGW 路由传输过程中吃掉了 TCP Timestamp参数, 叠加起来导致了这个问题。

我们面对问题需要保持敬畏之心,深挖到底!

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

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

相关文章

进程控制学习笔记

文章目录 进程退出孤儿进程僵尸进程进程回收waitpid()函数 进程退出 子进程的退出需要父进程对其资源的释放,子进程只能对用户区的数据进行释放,无法完成对内核区的释放。 可以获取到。 两个退出的区别: 系统调用不会刷新缓冲区&#xff…

Kubernetes对象深入学习之四:对象属性编码实战

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是《Kubernetes对象深入学习》系列的第四篇,前面咱们读源码和文档,从理论上学习了kubernetes的对象相关的知识&#xff…

python识别极验4滑块验证码实战

闲得无聊,趁着休息研究了一下极验4滑块验证码的安全性,是否有机器识别、自动化拖拽的可能性。首先看一下效果 如何识别验证码 1、下载图片 下载图片可以参考博客《采集极验4滑块验证码图片数据》 2、标记图片 3、标记滑动距离 实现代码 __author__ &…

【C++】C++11

文章目录 C111. 统一的列表初始化1.1 {}初始化 2. 声明2.1 auto2.2 decltype2.3 nullptr 3. 右值引用和移动语义3.1 左值引用和右值引用3.2 左值引用与右值引用比较3.3 右值引用使用的场景和意义3.4 完美转发 4. 可变参数模板5. lambda表达式5.1 函数对象与lambda表达式 6. 线程…

STM32入门之创建工程模板

1.STM32固件库的结构图如下。从图中可以看出,我们在配置STM32的固件库时需要配置用户层、CMSIS层的文件。配置库文件即正确的配置这些函数的文件。CMSIS(Cortex Microcontroller Software Interface Standard)是ARM公司提供的微控制器软件接口标准,所有使…

Data Structure, Algorithm,and Applications in C++

在学习这本书进阶内容之前,我们可以跟着它的第一章部分再巩固和复习。本书由Sartaj Sahni撰写,由王立柱和刘志红翻译。全书通俗易懂,内容丰富,是巩固C内容的不二选择。希望本文对各位有所帮助。 目录 1.函数与参数 1.1.传值参数…

C++初探

目录 经典开头 — C的历史 作用域运算符 using的用法 命名空间 - namespace 命名空间的基本使用 特殊的命名空间 - 无名命名空间 全部展开和部分展开 std — C所有的标准库都在std命名空间内 省缺值 - 默认参数 占位参数 内联函数 - inline 函数重载 函数重载的用…

【Unity2D】相机移动以及设置相机边界

添加相机 添加相机时,首先需要在unity中添加 Cinemachine 包 第一次使用这个包时,需要在Package Manager中搜索并安装 安装Camera Mechine包后,添加2D Camera 设置跟随对象为Ruby (从Hierarchy中将Ruby拖动到Follow中&#xff0…

c函数学习

函数的概念 函数是c语言的功能单位,实现一个功能可以封装一个函数来实现。定义函数的时候一切以功能为目的,根据功能去定义函数的参数和返回值 函数的分类 从定义角度分类:库函数(c库实现的),自定义函数&…

一、大数据技术之Flume(简介)

第1章 Flume概述 1.1 Flume定义 Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集、聚合和传输的系统。Flume基于流式架构,灵活简单。 1.2 Flume基础架构 Flume组成架构如下图所示。 1.2.1 Agent Agent是一个JVM进程&…

NLP实战8:图解 Transformer笔记

目录 1.Transformer宏观结构 2.Transformer结构细节 2.1输入 2.2编码部分 2.3解码部分 2.4多头注意力机制 2.5线性层和softmax 2.6 损失函数 3.参考代码 🍨 本文为[🔗365天深度学习训练营]内部限免文章(版权归 *K同学啊* 所有&#…

在外远程NAS群晖Drive - 群晖Drive挂载电脑磁盘同步备份【无需公网IP】

文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…

【Hadoop 01】简介

目录 1 Hadoop 简介 2 下载并配置Hadoop 2.1 修改/etc/profile 2.2 修改hadoop-env.sh 2.3 修改core-site.xml 2.4 修改hdfs-site.xml 2.5 修改mapred-site.xml 2.6 修改yarn-site.xml 2.7 修改workers 2.8 修改start-dfs.sh、stop-dfs.sh 2.9 修改start-yarn.sh、s…

Elemui表单合并

原代码形式 <template><el-table:data"tableData"borderstyle"width: 100%"><el-table-columnprop"date"label"日期"width"180"></el-table-column><el-table-columnprop"name"label…

WebGL 概念和基础入门

WebGL 概念和基础入门 WebGL 是什么 对于 WebGL 百度百科给出的解释是 WebGL 是一种 3D 绘图协议&#xff0c;而对此维基百科给出的解释却是一种 JavaScript API。由于 WebGL 技术旨在帮助我们在不使用插件的情况下在任何兼容的网页浏览器中开发交互式 2D 和 3D 网页效果&…

react 实现小球加入购物车动画

代码 import React, { useRef } from react;const ProductLayout () > {const box useRef(null);const createBall (left, top) > {const ball document.createElement(div);ball.style.position absolute;ball.style.left left - 10 px;ball.style.top top - 1…

【机器学习】了解 AUC - ROC 曲线

一、说明 在机器学习中&#xff0c;性能测量是一项基本任务。因此&#xff0c;当涉及到分类问题时&#xff0c;我们可以依靠AUC - ROC曲线。当我们需要检查或可视化多类分类问题的性能时&#xff0c;我们使用AUC&#xff08;曲线下面积&#xff09;ROC&#xff08;接收器工作特…

使用 Vue 创建一个简单的 Loading 动画

使用 Vue 创建一个简单的 Loading 动画 1. 开始之前 确保 正确安装了 Vue 3知道如何启动一个新的 Vue 项目&#xff08;或在项目中使用Vue&#xff09;了解 Vue 3 的 Composition API&#xff08;本文将使用&#xff09; 2. 设计组件 该组件应该包含三个部分 控制逻辑旋转…

win10 安装 langchain-chatglm 遇到的问题

win10 安装 langchain-chatglm 避坑指南&#xff08;2023年6月21日最新版本&#xff09;_憶的博客-CSDN博客官网看起来安装很简单&#xff0c;网上教程也是&#xff0c;但实际上我耗费了两天时间&#xff0c;查阅了当前网络上所有可查阅的资料&#xff0c;重复「安装-配置-卸载…

Spring Security 构建基于 JWT 的登录认证

一言以蔽之&#xff0c;JWT 可以携带非敏感信息&#xff0c;并具有不可篡改性。可以通过验证是否被篡改&#xff0c;以及读取信息内容&#xff0c;完成网络认证的三个问题&#xff1a;“你是谁”、“你有哪些权限”、“是不是冒充的”。 为了安全&#xff0c;使用它需要采用 …