一、Flink 任务链接的背景与需求
(图片来自实时即未来)
在 Flink 作业运行过程中,TaskManager 之间会不断进行数据交换。对于流计算场景来说,数据之间的交互需要非常及时,但如果每一次交互都立即传输,对网络资源的消耗会非常大。当网络开销到达峰值时,及时性反而会变差。
Flink 在流计算中面临着数据交换的挑战,需要一种既能保证实时性又能高效利用资源的机制。例如,在一个大规模的流计算应用中,可能有多个 TaskManager 同时处理不同的任务,这些任务之间需要频繁地交换数据。如果没有一个高效的数据交换机制,网络资源可能会被迅速耗尽,导致延迟增加,影响整个系统的性能。
为了解决这个问题,Flink 引入了任务链接机制。这种机制可以降低本地通信的开销,提高数据交换的效率。任务链接机制有一定的条件,每个算子必须有相同的并行度,并且算子之间是通过本地转发通道连接并交互数据。满足条件的算子里的函数会被 “融合” 到同一个任务中,在同一个线程里执行,每个函数之间只需要通过简单的方法调用,就可以进行数据交互,各函数之间的数据传输基本不存在序列化和通信(网络通信)的开销。
Flink 默认是开启了任务链接,如果有业务不需要任务链接,需要自行禁用。综上所述,Flink 的任务链接机制是为了满足流计算中对高效数据交换的需求,确保流计算过程中 “实时性” 和 “资源利用率” 的平衡,从而达到更高的吞吐和最佳的效率。
二、Flink 任务链接的实现方式
(一)任务链接的条件
Flink 任务链接机制要求严格,首先每个算子必须有相同的并行度。这意味着在一个流计算任务中,参与任务链接的各个算子所处理的子任务数量必须一致。例如,一个具有并行度为 4 的 map 算子和一个并行度为 2 的 filter 算子就无法进行任务链接。因为并行度不同会导致数据在不同数量的子任务之间流动,无法满足任务链接的一致性要求。
同时,算子之间必须通过本地转发通道连接并交互数据。这就要求数据在传递过程中,能够在本地进行快速转发,而不是通过网络进行传输。如果数据需要通过网络传输,那么就会引入网络延迟和开销,无法满足任务链接降低本地通信开销的目的。例如,在一个由 source、map、filter 和 sink 组成的流计算任务中,如果 source 和 map 之间的数据可以通过本地转发通道进行传递,并且它们的并行度相同,那么这两个算子就有可能进行任务链接。
(二)融合函数与执行方式
当满足任务链接的条件时,算子里的函数会被融合到同一个任务中,在同一线程执行。这种方式极大地提高了数据交换的效率。例如,在一个包含多个算子的流计算任务中,如果这些算子满足任务链接的条件,那么它们的函数就会被融合到一个任务中。在执行过程中,一个函数产生的数据可以通过简单的方法调用直接传递给下一个函数,无需进行序列化和网络通信。
以一个简单的 word count 任务为例,假设包含 source(读取文本数据)、flatMap(将文本拆分成单词)、map(转换单词为键值对)和 reduce(对相同单词的计数进行累加)这几个算子。如果这些算子满足任务链接的条件,那么它们的函数就会被融合到一个任务中。在执行时,source 函数读取到的文本数据可以直接通过方法调用传递给 flatMap 函数,flatMap 函数将文本拆分成单词后,再通过方法调用将单词传递给 map 函数,以此类推。这样,数据在各个函数之间的传递非常快速,基本不存在序列化以及通信(网络通信)的开销。
三、Flink 任务链接的优势
任务链接可以实现对资源的合理分配,确保繁重子任务在任务管理器间公平分配。在默认情况下,Flink 允许子任务共享 slot,只要是来自于同一作业即可。通过任务链接,将相关性很强的 transformation 操作绑定在一起,形成一个任务。这样,非密集 subtask(如 source/map ())和密集型 subtask(如 window)可以更好地共享资源,避免非密集 subtask 占用过多资源而密集型 subtask 资源不足的情况。
例如,在一个具有不同并行度的流计算任务中,如果没有任务链接,可能会出现某些算子的子任务占用过多资源,而其他算子的子任务资源不足的情况。而通过任务链接,将并行度相同且满足条件的算子链接成任务,可以充分利用分配的资源,提高资源利用率。假设一个任务中包含 source、map、filter 和 window 等算子,其中 window 算子通常是比较密集的计算任务。如果没有任务链接,source 和 map 等非密集算子可能会占用过多资源,导致 window 算子资源不足。而通过任务链接,将这些算子合理地分配到不同的任务中,可以确保繁重子任务在任务管理器间公平分配,提高整个系统的性能和资源利用率。
四、Flink 任务链接的应用场景与注意事项
(一)默认开启状态及业务需求考量
Flink 默认会将多个算子进行串联,形成任务链。这种默认开启的状态是为了在大多数情况下提高流计算的效率和性能。然而,在实际业务中,并非所有场景都适合任务链接。例如,当需要对某个特定算子进行独立的资源分配和调度时,或者当某个算子的执行逻辑与其他算子差异较大,可能会影响整个任务链的性能时,就需要考虑禁用任务链接。
(二)禁用方法
- 禁用全局任务链:可以使用 StreamExecutionEnvironment.disableOperatorChaining() 方法来禁用整条工作链。关闭全局任务链后,如果需要创建对应 Operator Chain,则需要用户先指定操作符,然后再调用 startNewChain() 方法创建。但需要注意的是,startNewChain 方法创建的链条只对调用方法的前一个操作符和后一个操作符有效,不影响其他的操作符。
- 禁用局部任务链:如果不想关闭整体算子上的链条,只是想关闭部分算子上链条绑定,可以使用 disableChaining() 方法禁用当前操作符上的链条。
(三)注意事项
禁用任务链接可能会对性能产生一定的影响。任务链接 / 操作符链接将一个或多个任务引入到单个线程中,减少了在流计算中记录反 / 序列化的影响。当一条记录到达映射实例时,在完成映射函数之后,将直接调用过滤器函数(简单方法调用),而不需要序列化和反序列化操作。如果禁用链接,则无法将记录直接传递给其他操作,这将导致不良的性能影响。
在决定是否禁用任务链接时,需要充分考虑业务需求和性能影响。如果不确定是否应该禁用任务链接,可以通过性能测试来评估不同设置下的系统性能,以便做出更合理的决策。同时,禁用全局任务链会影响整体任务执行的情况,在禁用前,要清楚任务执行的流程,否则可能造成非预期的结果。
五、Flink 任务链接在整体架构中的地位
任务链接在 Flink 运行时架构中的作用
任务链接在 Flink 运行时架构中起着至关重要的作用。它优化了数据在不同组件之间的传输方式,提高了系统的性能和效率。在 Flink 的运行时架构中,任务链接与作业管理器(JobManager)、资源管理器(ResourceManager)、任务管理器(TaskManager)以及分发器(Dispatcher)等组件协同工作。
首先,任务链接与任务管理器密切相关。TaskManager 是 Flink 中的工作进程,负责执行作业中的任务。任务链接使得满足条件的算子里的函数能够在同一个任务中执行,减少了线程间切换和缓冲开销,提高了数据交换的效率。这有助于 TaskManager 更高效地利用资源,提高任务的执行速度。
其次,任务链接也与资源管理器相互作用。ResourceManager 负责管理任务管理器的插槽(slot),任务链接可以优化资源的分配,确保繁重子任务在任务管理器间公平分配。通过将相关性很强的 transformation 操作绑定在一起,形成一个任务,非密集 subtask 和密集型 subtask 可以更好地共享资源,避免资源浪费。
与其他组件协同工作的情况
在任务提交流程中,任务链接也发挥着重要作用。当客户端将任务提交给 JobManager 后,JobManager 会将作业图(JobGraph)转换成物理执行图(ExecutionGraph),并向资源管理器请求资源。资源管理器会将有空闲插槽的 TaskManager 分配给 JobManager,TaskManager 启动后向 JobManager 发送心跳包,并等待分配任务。在这个过程中,任务链接可以使得任务在 TaskManager 上的执行更加高效。
例如,在 YARN 模式任务提交流程中,Client 向 HDFS 上传 Flink 的 Jar 包和配置,之后向 Yarn ResourceManager 提交任务。ResourceManager 分配 Container 资源并通知对应的 NodeManager 启动 ApplicationMaster,ApplicationMaster 启动后加载 Flink 的 Jar 包和配置构建环境,然后启动 JobManager。之后 ApplicationMaster 向 ResourceManager 申请资源启动 TaskManager,NodeManager 加载 Flink 的 Jar 包和配置构建环境并启动 TaskManager。TaskManager 启动后,任务链接可以使得数据在不同的 TaskManager 之间的交换更加高效,减少网络开销,提高系统的性能。
总之,任务链接在 Flink 整体架构中占据着重要的地位,与其他组件协同工作,共同为流计算任务的高效执行提供支持。
往期推荐