网络流控的概念与背景

1.为什么需要网络流控

flink反压对kafka的影响 简述flink的反压原理_Network


如果 Receive Buffer 是有界的,这时候新到达的数据就只能被丢弃掉了。

如果 Receive Buffer 是无界的,Receive Buffer 会持续的扩张,最终会导致 Consumer 的内存耗尽。

2.网络流控的实现:静态限速

flink反压对kafka的影响 简述flink的反压原理_Network_02


我们在Producer端实现一个静态限流,Producer经过限流器流量降低到和Consumer端相同,这样的话 Producer 端的发送速率跟 Consumer 端的处理速率就可以匹配起来了,就不会导致上述问题。但是这个解决方案有两点限制:

1.事先无法预估 Consumer 到底能承受多大的速率;

2.Consumer 的承受能力通常会动态地波动。

3.网络流控的实现:动态反馈/自动反压

flink反压对kafka的影响 简述flink的反压原理_flink_03


我们需要Consumer及时的给Producer做一个feedback,告知Producer能够承受的速率是多少(flink的反压就采用动态反馈)。

动态反馈分为两种:

1.负反馈:接受速率小于发送速率时发生,告知 Producer 降低发送速率;

2正反馈:发送速率小于接收速率时发生,告知 Producer 可以把发送速率提上来。

Flink反压机制

TCP流控机制

TCP 包的格式结构,有 Sequence number 这样一个机制给每个数据包做一个编号,还有 ACK number 这样一个机制来确保 TCP 的数据传输是可靠的,除此之外还有一个很重要的部分就是 Window Size,接收端在回复消息的时候会通过 Window Size 告诉发送端还可以发送多少数据。

flink反压对kafka的影响 简述flink的反压原理_Network_04

TCP流控:滑动窗口

flink反压对kafka的影响 简述flink的反压原理_Network_05


flink反压对kafka的影响 简述flink的反压原理_TCP_06


接收端根据消费速率确定一个窗口大小,每次通信时会返回发送端一个可用的窗口大小,发送端根据窗口大小发送数据。

flink反压对kafka的影响 简述flink的反压原理_接收端_07


flink反压对kafka的影响 简述flink的反压原理_flink_08

当消费端消费出现问题,window=0的时候,发送端会定期的发送 1 个字节的探测消息,这时候接收端就会把 window 的大小进行反馈。当接收端的消费恢复了之后,接收到探测消息就可以将 window 反馈给发送端端了从而恢复整个流程。TCP 就是通过这样一个滑动窗口的机制实现 feedback。

Flink TCP-based反压机制(before V1.5)

flink反压对kafka的影响 简述flink的反压原理_flink_09


flink反压对kafka的影响 简述flink的反压原理_Network_10


当Producer速率大于Consumer速率的时候,一段时间后 InputChannel 的 Buffer 被用尽,于是他会向 Local BufferPool 申请新的 Buffer ,Local BufferPool 用尽向 Network BufferPool申请,当Network BufferPool也用尽的时候,这时 Netty AutoRead 就会被禁掉,Netty 就不会从 Socket 的 Buffer 中读取数据了。过不多久Socket的buffer也会被用尽,这是window=0发送给发送端。这时候socket停止发送。

flink反压对kafka的影响 简述flink的反压原理_flink反压对kafka的影响_11


很快发送端的 Socket 的 Buffer 也被用尽,Netty 检测到 Socket 无法写了之后就会停止向 Socket 写数据。所有的数据就会阻塞在 Netty 的 Buffer 当中,很快Netty的buffer也不能在写数据了,数据就会积压到ResultSubPartition中。和接收端一样ResultSubPartition会不断的向 Local BufferPool 和 Network BufferPool 申请内存。

Local BufferPool 和 Network BufferPool 都用尽后整个 Operator 就会停止写数据,达到跨 TaskManager 的反压。

Flink Credit-based 反压机制(since V1.5)

flink反压对kafka的影响 简述flink的反压原理_Network_12


这个机制简单的理解起来就是在 Flink 层面实现类似 TCP 流控的反压机制来解决上述的弊端,Credit 可以类比为 TCP 的 Window 机制。

flink反压对kafka的影响 简述flink的反压原理_TCP_13


每一次 ResultSubPartition 向 InputChannel 发送消息的时候都会发送一个 backlog size 告诉下游准备发送多少消息,下游就会去计算有多少的 Buffer 去接收消息,算完之后如果有充足的 Buffer 就会返还给上游一个 Credit 告知他可以发送消息。

flink反压对kafka的影响 简述flink的反压原理_接收端_14

当上流速率大于下游速率的时候,下游的 TaskManager 的 Buffer 已经到达了申请上限,这时候下游就会向上游返回 Credit = 0,ResultSubPartition 接收到之后就不会向 Netty 去传输数据,上游 TaskManager 的 Buffer 也很快耗尽,达到反压的效果,这样在 ResultSubPartition 层就能感知到反压,不用通过 Socket 和 Netty 一层层地向上反馈,降低了反压生效的延迟。同时也不会将 Socket 去阻塞,解决了由于一个 Task 反压导致 TaskManager 和 TaskManager 之间的 Socket 阻塞的问题。

Flink反压机制演变

flink1.5之前使用的是TCP的反压机制,但是反压实现依赖于底层TCP的反压,流程较长,延迟比较高。且TaskManager中一个任务线程数据积压就会造成TCP缓冲区数据积压,阻塞Socket,使其余的Task也都无法传输数据,同时barrier也无法传输到下流,造成下流做checkpoint的延时增大。
因此,1.5之后flink的反压改为了信任度机制,在应用层通过信任度模拟TCP的流量控制,实现反压。

生产环境反压识别和排查

识别反压

1、Flink 1.9 UI / Flink 1.11 旧 UI

如下图所示,你可以直接在 Flink Web 中,针对任何一个 Task 做反压检测。该检测机制是需要在 Flink Web 上手动触发,触发后 TaskManager 使用 Thread.getStackTrace 来抽样检测 Task Thread 是否处于等待 NetworkBuffer 中。根据抽样比例,来判断反压状态是处于 OK, LOW, HIGH 三个状态。Ratio 是代表抽样 n 次,遇到等待 NetworkBuffer 次数的比例。

flink反压对kafka的影响 简述flink的反压原理_接收端_15

可以从 Sink->Source 反着依次进行检查,遇到的第一个处于 HIGH 状态的 Task 可能(并不绝对) 就是触发反压的根本原因,由于该 Task 处于反压状态,它会将该状态不断向上传递,直到 Source。

当然这种检测方式,存在它本身的缺点:

  • 即时触发,并不能观察历史情况。
  • 如果遇到并发较多的 Task 时,可能需要等很久才能检测出来。
  • 影响作业正常运行状态。
2、Flink Network Metric

从上面我们知道,处于中间的 Task 都会有一个 InputQueue 和 OutputQueue,我们可以通过使用 InputQueueUsage 和 OutputQueueUsage 比例来判断。

Task Status

OutputQueueUsage < 1.0

OutputQueueUsage == 1.0

InputQueueUsage < 1.0

正常

处于反压,其根本原因可能是该 Task 下游处理能力不足导致,持续下去,该 Task 将会向上游传递反压

InputQueueUsage == 1.0

处于反压,持续下去,该 Task 会向上游传递反压,而且该 Task 可能是反压的源头

处于反压,原因可能是被下游阻塞

所以当作业处于反压下,我们需要从 Sink -> Source 逐步找到第一个 InputQueueUsage High,OutputQueueUsage Low 的 Task。优先查看下游的问题,这个更有可能是根本问题。inputPoolUsage > 0 的最下游的task。加上tag: host=*, 看是否某一台机器的高,如果是,重点检查这台机器和这上面的taskmanager, 比如,检查这台机器的负载

flink反压对kafka的影响 简述flink的反压原理_flink反压对kafka的影响_16

常见反压原因

资源不足

Container CPU 不足,导致计算能力不足,出现反压。点击 Dtop Metric 观察单 Container CPU 使用和 Application CPU 使用情况。

flink反压对kafka的影响 简述flink的反压原理_接收端_17

flink反压对kafka的影响 简述flink的反压原理_Network_18

由于 cgroup 策略给单个 Container 预留了 20% 的 Buffer,故可以按照 100% 来区分高负载和低负载。

Container CPU < 100% * tm_cores

Container CPU > 100% * tm_cores

Application CPU < 80% * total_cores


负载不均

Application CPU > 80% * total_cores

na(不会出现)


当遇到资源不足时,可以选择缩小单 tm_slots,增加 tm_num 个数。
如果是负载不均衡的话,请参照下面来排查。

负载不均

当出现负载不均衡时,需要分析两个原因:(1)是否数据倾斜;(2)是否 Task 调度不均衡。

(1)数据倾斜请查看 Flink Metric 中的各个 Operater 的 QPS 的 Max 和 AVG 值,差别太大的话,则代表数据倾斜。如下图所示,该 bolt 的 qps max 和 avg 差距 10 倍,则说明产生了数据倾斜。

flink反压对kafka的影响 简述flink的反压原理_flink反压对kafka的影响_19

(2)Task 是否调度不均衡

如果 Task 并发度与 Container 并发度不能成正比,则代表不同 Container 分配的 Task 个数不同。

GC 压力

Flink Dashboard 上,默认有 YGC, FGC 两个指标,请查看相关指标,一般我们只需要关注 FULL GC 指标。

flink反压对kafka的影响 简述flink的反压原理_flink_20

Task Latency 较高

用户自己写的某些 Task 执行时间较长,如果 Task 之间有 IO 操作,需要检查 IO Latency 是否过于高。该 Latency 可以查看 Flink Metric Dashboard,上面记录了每个 Task 的执行时间,从这里很清楚看到 map latency 是 10s。遇到这种情况,要么优化 task 处理能力,要么增加并发。

flink反压对kafka的影响 简述flink的反压原理_TCP_21

系统资源

遇到单机整体负载较高(常见于 Bigbang 队列和共享 Share 队列),该情况一般会产生局部 Partition 延迟。
遇到单机网络异常,一般会导致 Network Stack 变慢,导致延迟。
遇到这种问题的话,一般用户很难自己排查,请联系 Flink Oncall。

单线程瓶颈

Java Flink / SQL 任务一般情况下单 Task 是由单线程来执行的,单线程跑满也只能跑 1 个 CPU。
所以你需要观察单 Container 分配 Task 个数,以及该 Container CPU 使用情况,如果 Container CPU 使用情况达到该 Container 分配 Task 个数的 Cores,则会出现单线程瓶颈,你需要增加并发。