一、背压机制实现总结
- Flink对于背压的处理是通过在任务传递之间设置有界容量的数据缓冲区, 当整个管道中有一个下游任务速度变慢,会导致缓存区数据变满,上游任务获取不到可用的缓冲区,自然而然地被阻塞和降速, 这就实现了背压。
不同taskManager通信通过Netty, Netty的 Buffer 是无界的,但可以设置 Netty 的高水位
- 如果同一 Task 的不同 SubTask 被安排到同一个TaskManager,则它们与其他 TaskManager 的网络连接将被多路复用并共享一个TCP信道以减少资源使用。这样会导致其中一个子任务流程受阻,其余子任务也会被连累受阻。为此flink1.5后通过Credit的反压策略,即通过反馈Credit(即缓存区的数量)的方式,巧妙的跳过共享通道,将数据堆积在子任务缓存区中。
二、背压的定义
背压是指系统在临时负载峰值期间接收数据的速率高于其可以处理的速率的情况
三、如何处理背压
3.1 Flink任务流程天然的提供了应对背压能力
如果产生背压,理想的应对方法是flink整个任务流程会限制源以调整速度匹配管道中最慢的部分,达到稳态
源会被限制速度,与管道其他部分匹配,避免数据堆积。
根据介绍: Flink中的流在算子之间传输数据,就像连接线程的阻塞队列一样起到缓冲和限速的作用。当下游算子处理速度较慢时,上游算子会被限速。这种机制天然地为Flink提供了背压能力。
但是,为什么当下游算子处理速度较慢时,上游算子会被限速?
3.1.1 从flink任务执行流程看:
一个flink的任务流程如下:
-
记录“A”进入Flink,并由Task 1处理。
-
记录被序列化到一个缓冲区。
-
这个缓冲区被发给Task 2,然后Task 2从缓冲区读取记录。
flink通过在任务传递之间设置有界容量的数据缓冲区, 实现了流量控制和背压。如果下游任务的处理速度较慢,上游任务会被阻塞并降低速度,防止缓冲区溢出。这种机制天然地为Flink提供了背压能力。
当下游任务处理速度较慢时,上游任务获取不到可用的缓冲区,自然而然地被阻塞和降速,这就实现了背压。
Task 1在输出端有一个相关的缓冲池,Task 2在输入端有一个。如果有可用缓冲区对“A”进行序列化,我们就对其进行序列化并分发这个缓冲区。
3.1.2 flink任务执行的三种情况
- 远程交换:如果Task 1和Task 2运行在不同的工作节点上,那么缓冲区可以在被发送到网络上时就回收(TCP通道)。在接收端Task 2,数据从网络复制到输入缓冲池的一个缓冲区。如果没有可用缓冲区,就中断从TCP连接的读取。输出端Task 1通过一个简单的水位线机制永远不会在网络上放太多数据。如果网络上有超过阈值的数据在等待传输,我们在输入数据进网络之前等待,直到数据量低于一个阈值。这保证了网络上永远不会有过多的数据。如果接收端由于没有可用缓冲区而不消费新数据,这会放慢发送者的速度。
TCP 的 Socket 通信有动态反馈的流控机制,会把容量为0的消息反馈给上游发送端,所以上游的 Socket 就不会往下游再发送数据 。
Netty 的 Buffer 是无界的,但可以设置 Netty 的高水位,即:设置一个 Netty 中 Buffer 的上限。所以每次 ResultSubPartition 向 Netty 中写数据时,都会检测 Netty 是否已经到达高水位,如果达到高水位就不会再往 Netty 中写数据,防止 Netty 的 Buffer 无限制的增长。
- 本地交换:如果Task 1和Task 2都在同一个工作节点(TaskManager)上运行,缓冲区可以直接交给下一个任务,而不需要通过TCP通道传输,本地交换背压处理机制与远程交换一致。一旦Task 2消费完毕,缓冲区就会被回收。如果Task 2比Task 1慢,那么回收缓冲区的速度会低于Task 1填充它们的速度,这时所有的缓冲区都会被填满,从而导致Task 1获取不到可用的缓冲区而放慢速度。
- Task内部流程<