Flink 流处理为什么需要⽹络流控?
Flink V1.5
版之前⽹络流控介绍
Flink V1.5
版之前的反压策略存在的问题
Credit
的反压策略实现原理
,
Credit
是如何解决
Flink 1.5
之前的问题?
Flink
如何在吞吐量和延迟做权衡?
Flink
反压相关
Metrics
介绍 基于 Flink
的流控机制和反压如何定位
Flink
任务的瓶颈。或者说,如果⼀个平时正常的
Flink 任务突然出现延迟了,怎么来定位问题?到底是 Kafka
读取数据慢,还是中间某个计算环节 ⽐较消耗资源使得变慢,还是由于最后的写⼊外部存储时⽐较慢?
Flink
流处理为什么需要⽹络流控?
分析⼀个简单的
Flink
流任务,下图是⼀个简单的
Flink
流任务执⾏图:任务⾸先从
Kafka
中读取数据、 map 算⼦对数据进⾏转换、
keyBy
按照指定
key
对数据进⾏分区(相同
key
的数据经过
keyBy
后分到 同⼀个 subtask
实例中),
keyBy
后对数据接着进⾏
map
转换,然后使⽤
Sink
将数据输出到外部存 储。
众所周知,在⼤数据处理中,⽆论是批处理还是流处理,单点处理的性能总是有限的,我们的单个
Job ⼀般会运⾏在多个节点上,多个节点共同配合来提升整个系统的处理性能。图中,任务被切分成 4
个可 独⽴执⾏的 subtask
(
A0
、
A1
、
B0
、
B1
),在数据处理过程中,就会存在
shufflfflffle
(数据传输)的过 程。例如,subtask A0
处理完的数据经过
keyBy
后发送到
subtask B0
、
B1
所在节点去处理。
那么问题来了,下图中,上游
Producer
向下游
Consumer
发送数据,在发送端和接受端都有相应的 Send Buffffer 和
Receive Buffffer
,但是上游
Producer
⽣成数据的速率⽐下游
Consumer
消费数据的速 率快。Producer
⽣产数据
2MB/s
,
Consumer
消费数据
1MB/s
,
Receive Buffffer
只有
5MB
,所以过 了5
秒后,接收端的
Receive Buffffer
满了。(可以把下图中的
Producer
当做上⾯案例中的
subtask A0,把下图中的
Consumer
当做上⾯案例中的
subtask B0
) 下游接收区的 Receive Buffffer
有限,如果上游⼀直有源源不断的数据,那么将会⾯临着以下两个情况:
1.
下游消费者会丢弃新到达的数据,因为下游消费者的缓冲区放不下
2.
为了不丢弃数据,所以下游消费者的
Receive Buffffer
持续扩张,最后耗尽消费者的内存,
OOM
,
程序挂掉
常识告诉我们,这两种情况在⽣产环境都是不能接受的,第⼀种会把数据丢弃、第⼆种会把我们的应⽤ 程序挂掉。所以,该问题的解决⽅案不应该是下游 Receive Buffffer
⼀直累积数据,⽽是上游
Producer 发现下游 Consumer
处理⽐较慢之后,应该在
Producer
端做出限流的策略,防⽌在下游
Consumer 端⽆限制的数据堆积。 那上游 Producer
端该如何做限流呢?可以采⽤下图所示静态限流的策略: 静态限速的思想就是,提前已知下游 Consumer
的消费速率,然后通过在上游
Producer
端使⽤类似令 牌桶的思想,限制 Producer
端⽣产数据的速率,从⽽控制上游
Producer
端向下游
Consumer
端发送 数据的速率。但是静态限速会存在问题:
1.
通常⽆法事先预估下游
Consumer
端能承受的最⼤速率
2.
就算通过某种⽅式预估出下游
Consumer
端能承受的最⼤速率,下游应⽤程序也可能会因为⽹络
抖动、
CPU
共享竞争、内存紧张、
IO
阻塞等原因造成下游应⽤程序的吞吐量降低,然后⼜会出现 上⾯所说的下游接收区的 Receive Buffffer
有限,上游⼀直有源源不断的数据发送到下游的问题, 还是会造成下游要么丢数据,要么为了不丢数据 buffffer
不断扩充导致下游
OOM
的问题 综上所述,我们发现了,上游 Producer
端必须有⼀个限流的策略,且静态限流是不可靠的,于是就需 要⼀个动态限流的策略。可以采⽤下图动态反馈所示:下游
Consumer
端会频繁地向上游
Producer
端进⾏动态反馈,告诉
Producer
下游
Consumer
的负载 能⼒,从⽽ Producer
端动态调整向下游
Consumer
发送数据的速率实现
Producer
端的动态限流。当 Consumer 端处理较慢时,
Consumer
将负载反馈到
Producer
端,
Producer
端
会根据反馈适当降低
Producer
⾃身从上游或者
Source
端读数据的速率
来降低向下游
Consumer
发送数据的速率。当 Consumer 处理负载能⼒提升后,⼜及时向
Producer
端反馈,
Producer
会通过提升从上游或
Source 端读数据的速率来提升向下游发送数据的速率。通过这个动态反馈来提升整个系统的吞吐量。 补充⼀点,假如我们的 Job 分为
Task A
、
B
、
C
,
Task A
是
Source Task
、
Task B
处理数 据、Task C
是
Sink Task
。假如
Task C
由于各种原因吞吐量降低,会将负载信息反馈给
Task B
,
Task B 会降低向 Task C
发送数据的速率,此时如果
Task B
如果还是⼀直从
Task A
读取数据,那么按照同样的 道理,数据会把 Task B
的
Send Buffffer
和
Receive Buffffer
撑爆,⼜会出现上⾯描述的问题。所以,当 Task B 的
Send Buffffer
和
Receive Buffffer
被⽤完后,
Task B
会⽤同样的原理将负载信息反馈给
Task A,
Task A
收到
Task B
的负载信息后,会降低 给
Task B
发送数据的速率,以此类推。 上⾯这个流程,就是 Flink
动态限流(反压机制)的简单描述。我们可以看到
Flink
的反压其实是从下游 往上游传播的,⼀直往上传播到 Source Task
后,
Source Task
最终会降低从
Source
端读取数据的速 率。如果下游 Task C
的负载