Flink 网络传输优化技术

本文涉及的产品
数据传输服务 DTS,数据同步 small 3个月
推荐场景:
数据库上云
数据传输服务 DTS,数据迁移 small 3个月
推荐场景:
MySQL数据库上云
实时计算 Flink 版,5000CU*H 3个月
简介: 5万人关注的大数据成神之路,不来了解一下吗? 5万人关注的大数据成神之路,真的不来了解一下吗? 5万人关注的大数据成神之路,确定真的不来了解一下吗? 作为工业级的流计算框架,Flink 被设计为可以每天处理 TB 甚至 PB 级别的数据,所以如何高吞吐低延迟并且可靠地在算子间传输数据是一个非常重要的课题。

5万人关注的大数据成神之路,不来了解一下吗?

5万人关注的大数据成神之路,真的不来了解一下吗?

5万人关注的大数据成神之路,确定真的不来了解一下吗?

作为工业级的流计算框架,Flink 被设计为可以每天处理 TB 甚至 PB 级别的数据,所以如何高吞吐低延迟并且可靠地在算子间传输数据是一个非常重要的课题。此外,Flink 的数据传输还需要支持框架本身的特性,例如反压和用于测量延迟的 latency marker。在社区不断的迭代中,Flink 逐渐积累了一套值得研究的网络栈(Network Stack),本文将详细介绍 Flink Network Stack 的实现细节以及关键的优化技术。

本文主要基于 Nico Kruber 在去年 9 月 Flink Forward Berlin 上的分享 [1],涉及到的技术主要有 1.5 版本引入的 Credit-based 数据流控制以及在延迟和吞吐方面做的优化。在开始之前,我们首先来回顾下 Flink 计算模型里的核心概念,这写概念会在后续被频繁地提及。

Flink 计算模型

Flink 计算模型分为逻辑层和执行层,逻辑层主要用于描述业务逻辑,而执行层则负责作业具体的分布式执行。

用户提交一个作业以后,Flink 首先在 client 端执行用户 main 函数以生成描述作业逻辑的拓扑(StreaGraph),其中 StreamGraph 的每个节点是用户定义的一个算子(Operator)。随后 Flink 对 StreamGraph 进行优化,默认将不涉及 shuffle 并且并行度相同的相邻 Operator 串联起来成为 OperatorChain 形成 JobGraph,其中的每个节点称为 Vertice,是 OperatorChain 或独立的 Operator。

image

                           图1.分布式运行时

每个 Vertice 在执行层会被视为一个 Task,而一个 Task 对应多个 Subtask,Subtask 的数目即是用户设置的并行度。Subtask 根据 Flink 的调度策略和具体的部署环境及配置,会被分发到相同或者不同的机器或者进程上,其中有上下游依赖关系的 Subtask 会有数据传输的需要,这是通过基于 Netty 的 Network Stack 来完成的。

Network Stack 主要包括三项内容,Subtask 的输出模式(数据集是否有界、阻塞或非阻塞)、调度类型(立即调度、等待上一阶段完成和等待上一阶段有输出)和数据传输的具体实现(buffer 和 buffer timeout)。

image

                              图2.网络栈概览

下文的内容会主要围绕数据传输部分展开,逐一介绍其中的优化技术。

Credit-based 数据流控制

在上文图二左半部分可以看到 Subtask 之间有一条独立的数据传输管道,其实这是逻辑视图,而在物理层 Flink 并不会为维护 Subtask 级别的 TCP 连接,Flink 的 TCP 连接是 TaskManager 级别的。对于每个 Subtask 来说,根据 key 的不同它可以输出数据到下游任意的 Subtask,因此 Subtask 在内部会维护下游 Subtask 数目的发送队列,相对地,下游 Subtask 也会维护上游 Subtask 数目的接收队列。相同两个 TaskManager 上不同的 Subtask 的数据传输会通过 Netty 实现复用和分用跑在同一条 TCP 连接上。

image

                        图3.网络传输物理视图

这种实现的问题在于当某个 Subtask 出现反压时,反压不仅会作用于该 Subtask 的 Channel,还会误伤到这个 TaskManager 上的其他 Subtask,因为整个 TCP 连接都被阻塞了。比如在图 3 中,因为 Subtask 4 一个 Channel 没有空闲 Buffer,使用同一连接的其他 3 个 Channel 也无法通信。为了解决这个问题,Flink 自 1.5 版本引入了 Credit-based 数据流控制为 TCP 连接提供更加细粒度的控制。

image

                        图4.Channel 初始状态

具体来说,在接受端的 Buffer 被划分为 Exclusive Buffer 和 Floating Buffer 两种,前者是固定分配到每条接受队列里面的,后者是在 Subtask 级别的 Buffer Pool 里供动态分配。发送队列里的数据称为 Blacklog,而接收队列里的 Buffer 称为 Credit。Credit-Based 数据流控制的核心思想则是根据接收端的空闲 Buffer 数(即 Credit)来控制发送速率,这和 TCP 的速率控制十分类似,不过是作用在应用层。

假设当前发送队列有 5 个 Blacklog,而接收队列有 2 个空闲 Credit。首先接收端会通知发送端可以发送 2 个 Buffer,这个过程称为 Announce Credit。随后发送端接收到请求后将 Channel Credit 设为 2,并发送 1 个 Buffer(随后 Channel Credit 减为 1 ),并将剩余 4 个 Backlog 的信息随着数据一起发给接收端,这个两个过程分为称为 Send Buffer 和 Announce Blacklog Size。接收端收到 Backlog Size 之后会向 Buffer Pool 申请 Buffer 以将队列拓展至可以容纳 Backlog Size 的数据,但不一定能全部拿到。因为队列目前有一个空闲 Buffer,因此只需要向 Buffer Pool 申请 3 个 Buffer。假设 3 个 Buffer 都成功申请到,它们会成为 Unannounced Credit,并在下一轮请求中被 Announce。

image

                      图5.Credit-based 流控制

当一条 Channel 发送端的 Announced Credit 与 接收端的 Unannounced Credit 之和不小于 Blacklog Size 时,该 Channel 处于正常状态,否则处于反压状态。

从总体上讲,Credit-based 数据流控制避免了阻塞 TCP 连接,使得资源可以更加充分地被利用,另外通过动态分配 Buffer 和拓展队列长度,可以更好地适应生产环境中的不断变化的数据分布及其带来的 Channel 状态抖动,也有利于缓减部分 Subtask 遇到错误或者处理速率降低造成的木桶效应。然而这个新的机制也会引入额外的成本,即每传输一个 Buffer 要额外一轮 Announce Credit 请求来协商资源,不过从官方的测试来看,整体性能是有显著提升的。

image

                        图6.Credit-based 流控制性能提升

重构 Task Thread 和 IO Thread 的协作模型

熟悉网络传输的同学应该对高吞吐和低延迟两者的 trade-off 十分熟悉。网络是以 batch 的形式来传输数据的,而每个 batch 都会带来额外的空间开销(header 等元数据)和时间开销(发送延迟、序列化反序列化延等),因此 batch size 越大则传输的开销越小,但是这也会导致延时更高,因为数据需要在缓存中等待的时间越久。对于实时类应用来说,我们通常希望延迟可以被限定在一个合理的范围内,因此业界大多数的做法是设置一个 batch timeout 来强制发送低于 batch size 的数据 batch,这通常需要额外设置设置一个线程来实现。

Flink 也不例外。在上图的 TCP 连接发送端是 Netty Server,而接收端是 Netty Client,两者都会有 event loop 不断处理网络 IO。以实时作业为例子,与 Netty 组件直接交互的是 StreamRecordWriter 和 StreamRecordReader (现已被 StreamWriter 和 StreamInputProcessor 代替),前者负责将 Subtask 最终输出的用 StreamRecord 包装的数据序列化为字节数组并交给 Netty Server,后者负责从 Netty Client 读取数据并反序列化为 StreamRecord。

image

                          图7.StreamRecordWriter

当发送数据时,StreamRecordWriter 将记录反序列化为字节数组,并拷贝至 Netty Server 的 Channel 的一个 Buffer 中,如果 Buffer 满了它会提醒 Netty Server 将其发送。此后 StreamRecordWriter 会重新从 BufferPool 申请一个空的 Buffer 来重复上述过程,直至作业停止。为了实现 batch timeout,Flink 设置了一个 OutputFlusher 线程,它会定时 flush 在 Channel 中的 Buffer,也就是通知 Netty Server 有新的数据需要处理。Netty Server 会在额外分配线程来读取该 Buffer 到其已写的位置并将相关内容发送,其后该未写满 Buffer 会继续停留在 Channel 中等待后续写入。

image

                            图8.OutputFlusher

这种实现主要有两个问题: 一是 OutputFlusher 和 StreamRecordWriter 主线程在 Buffer 上会有竞争条件,因此需要同步操作,当 Channel 数量很多时这会带来性能上的损耗;二是当我们需要延迟尽可能小时,会将 timeout 设为 0 (实际上提供了 flushAlways 选项),然后每写一条记录就 flush 一次,这样会带来很高的成本,最坏的情况下会造成 Netty Server 频繁触发线程来读取输入,相当于为每个 Buffer 设置一个 event loop。一个简单的优化想法是,既然 Netty Server 本来就有 event loop,为什么不让 Netty 线程自己去检测是否有新数据呢?因此 Flink 在 1.5 版本重构了这部分的架构,弃用了要求同步的 OutputFlusher 线程,改为使用 StreamRecordWriter 和 Netty 线程间的非线程安全交互方式来提高效率,其中核心设计是 BufferBuilder 和 BufferConsumer。

image

                    图9.BufferBuilder & BufferConsumer

BufferBuilder 和 BufferConsumer 以生产者消费者的模式协作,前者是会被 StreamRecordWriter 调用来写入 Buffer,后者会被 Netty Server 线程调用,两者通过 volatile int 类型的位置信息来交换信息。通过这种方式,StreamRecordWriter 不会被 OutputFlusher 阻塞,资源利用率更高,网络传输的吞吐量和延迟均可受益。

image

                      图10.重构前后性能对比

避免不必要的序列化和反序列化

众所周知,序列化和反序列化是成本很高的操作,尤其是对于实时计算来说,因此 Flink 在避免不必要的序列化和反序列化方面做了不少优化工作。

Object Reuse 模式(Stream API)

在作业拓扑优化阶段,Flink 会尽可能将多个 Operator 合并为 Operator Chain 来减少 Task 数,因为 Subtask 内的 Operator 运行在同一个线程,不需要经过网络传输。尽管 Chained Operator 之间没有网络传输,但不同 Operator 直接共享对象实例并不安全,因为对象可能同时被多个算子并发访问造成意想不到的后果,并且按照函数式编程的理念,Operator 不应该对外界造成副作用,一个典型的正面例子就是 Scala 中的 Pure Function [5],因此默认情况下两个 Chained Operator 的数据对象传递是通过深拷贝来完成的,而深拷贝则是通过一轮序列化和反序列实现。不过出于性能考虑,自 Flink 提供了 Object Resue Mode 来关闭 Chained Operator 间的数据拷贝。

image

                    图11.Object Reuse Mode

Object Resue Mode 属于高级选项,当使用 Object Reuse 时用户函数必须符合 Flink 要求的规范 [2],比如不能将输入的数据对象存到 State 中,再比如不能在输出对象之后仍对其进行修改。

要注意的是,Object Resue Mode 在 Stream API 中的行为和在 Batch API 中的行为并不完全一致,前者是避免了 Chained Operator 之间的深拷贝,但不同 Subtask 之间(即使在同一 JVM 内)仍然需要深拷贝,而后者是每一步都是复用之前的对象,是真正的意义上的 Object Reuse。为此了统一 Object Reuse 在两个 API 的语义,Flink 社区提出了 FLIP-21 [3],但由于具体方案没有达成共识目前还没有实现的计划。

输出到多个 Channel 时只序列化一次

由于 Flink 维护的 RecordWriter 是 Channel 级别的,当一条数据需要被输出到多个 Channel 时(比如 broadcast),同样的数据会被序列化多次,导致性能上的浪费。因此在 1.7 版本,Flink 将 RecordWriter 的写 Buffer 操作分为将数据反序列化为字节数组和将字节数组拷贝到 Channel 里两步,从而使得多个 Channel 可以复用同一个反序列化结果 [4]。

总结

在版本迭代中,Network Stack 一直在不断改进来适应新的特性或者提高性能。其中在 1.5 版本进行了比较多的改进,包括最重要的 Credit-based 流控制和重构 Task Thread 和 IO Thread 的协作模型。

作为底层基础架构,Network Stack 设计的好坏很大程度上决定了一个计算框架的性能上限,其重要性对于 Flink 开发者或者有意贡献代码的用户而言不必多说。而对于 Flink 用户而言,熟悉 Network Stack 也可以让你在开发阶段提前预计或者部署后及时发现应用的瓶颈,从而在应对生产环境的部署复杂性时更加游刃有余。

参考资料

1.Improving throughput and latency with Flink’s network stack
2.Operating on data objects in functions
3.FLIP-21 - Improve object Copying/Reuse Mode for Streaming Runtime
4.FLINK-9913 - Improve output serialization only once in RecordWriter
4.Pure Functions

欢迎您关注《大数据成神之路》
相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
目录
相关文章
|
30天前
|
边缘计算 容灾 网络性能优化
算力流动的基石:边缘网络产品技术升级与实践探索
本文介绍了边缘网络产品技术的升级与实践探索,由阿里云专家分享。内容涵盖三大方面:1) 云编一体的混合组网方案,通过边缘节点实现广泛覆盖和高效连接;2) 基于边缘基础设施特点构建一网多态的边缘网络平台,提供多种业务形态的统一技术支持;3) 以软硬一体的边缘网关技术实现多类型业务网络平面统一,确保不同网络间的互联互通。边缘网络已实现全球覆盖、差异化连接及云边互联,支持即开即用和云网一体,满足各行业需求。
|
2月前
|
负载均衡 网络协议 网络性能优化
动态IP代理技术详解及网络性能优化
动态IP代理技术通过灵活更换IP地址,广泛应用于数据采集、网络安全测试等领域。本文详细解析其工作原理,涵盖HTTP、SOCKS代理及代理池的实现方法,并提供代码示例。同时探讨配置动态代理IP后如何通过智能调度、负载均衡、优化协议选择等方式提升网络性能,确保高效稳定的网络访问。
279 2
|
7天前
|
机器学习/深度学习 计算机视觉
RT-DETR改进策略【Neck】| ECCV-2024 RCM 矩形自校准模块 优化颈部网络
RT-DETR改进策略【Neck】| ECCV-2024 RCM 矩形自校准模块 优化颈部网络
34 10
RT-DETR改进策略【Neck】| ECCV-2024 RCM 矩形自校准模块 优化颈部网络
|
11天前
|
机器学习/深度学习 算法 文件存储
YOLOv11改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型
YOLOv11改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型
37 10
YOLOv11改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型
|
7天前
|
机器学习/深度学习 算法 文件存储
RT-DETR改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型
RT-DETR改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型
19 4
RT-DETR改进策略【模型轻量化】| MoblieNetV3:基于搜索技术和新颖架构设计的轻量型网络模型
|
2月前
|
机器学习/深度学习 算法
基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真
本项目基于改进遗传优化的BP神经网络进行金融序列预测,使用MATLAB2022A实现。通过对比BP神经网络、遗传优化BP神经网络及改进遗传优化BP神经网络,展示了三者的误差和预测曲线差异。核心程序结合遗传算法(GA)与BP神经网络,利用GA优化BP网络的初始权重和阈值,提高预测精度。GA通过选择、交叉、变异操作迭代优化,防止局部收敛,增强模型对金融市场复杂性和不确定性的适应能力。
206 80
|
3天前
|
传感器 算法 物联网
基于粒子群算法的网络最优节点部署优化matlab仿真
本项目基于粒子群优化(PSO)算法,实现WSN网络节点的最优部署,以最大化节点覆盖范围。使用MATLAB2022A进行开发与测试,展示了优化后的节点分布及其覆盖范围。核心代码通过定义目标函数和约束条件,利用PSO算法迭代搜索最佳节点位置,并绘制优化结果图。PSO算法灵感源于鸟群觅食行为,适用于连续和离散空间的优化问题,在通信网络、物联网等领域有广泛应用。该算法通过模拟粒子群体智慧,高效逼近最优解,提升网络性能。
|
2天前
|
机器学习/深度学习 数据采集 算法
基于GWO灰狼优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
本项目基于MATLAB2022a,展示了时间序列预测算法的运行效果(无水印)。核心程序包含详细中文注释和操作视频。算法采用CNN-GRU-SAM网络,结合灰狼优化(GWO),通过卷积层提取局部特征、GRU处理长期依赖、自注意力机制捕捉全局特征,最终实现复杂非线性时间序列的高效预测。
|
1月前
|
机器学习/深度学习 数据采集 算法
基于GA遗传优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
本项目基于MATLAB2022a实现时间序列预测,采用CNN-GRU-SAM网络结构。卷积层提取局部特征,GRU层处理长期依赖,自注意力机制捕捉全局特征。完整代码含中文注释和操作视频,运行效果无水印展示。算法通过数据归一化、种群初始化、适应度计算、个体更新等步骤优化网络参数,最终输出预测结果。适用于金融市场、气象预报等领域。
基于GA遗传优化的CNN-GRU-SAM网络时间序列回归预测算法matlab仿真
|
25天前
|
存储 人工智能 安全
AI时代的网络安全:传统技术的落寞与新机遇
在AI时代,网络安全正经历深刻变革。传统技术如多因素身份认证、防火墙和基于密码的系统逐渐失效,难以应对新型攻击。然而,AI带来了新机遇:智能化威胁检测、优化安全流程、生物特征加密及漏洞管理等。AI赋能的安全解决方案大幅提升防护能力,但也面临数据隐私和技能短缺等挑战。企业需制定清晰AI政策,强化人机协作,推动行业持续发展。
57 16

热门文章

最新文章