如何分析及处理 Flink 反压?
反压(backpressure)是实时计算应用开发中,特别是流式计算中,十分常见的问题。反压意味着数据管道中某个节点成为瓶颈,处理速率跟不上上游发送数据的速率,而需要对上游进行限速。由于实时计算应用通常使用消息队列来进行生产端和消费端的解耦,消费端数据源是 pull-based 的,所以反压通常是从某个节点传导至数据源并降低数据源(比如 Kafka consumer)的摄入速率。
关于 Flink 的反压机制,网上已经有不少博客介绍,中文博客推荐这两篇1。简单来说,Flink 拓扑中每个节点(Task)间的数据都以阻塞队列的方式传输,下游来不及消费导致队列被占满后,上游的生产也会被阻塞,最终导致数据源的摄入被阻塞。而本文将着重结合官方的博客[4]分享笔者在实践中分析和处理 Flink 反压的经验。
反压的影响
反压并不会直接影响作业的可用性,它表明作业处于亚健康的状态,有潜在的性能瓶颈并可能导致更大的数据处理延迟。通常来说,对于一些对延迟要求不太高或者数据量比较小的应用来说,反压的影响可能并不明显,然而对于规模比较大的 Flink 作业来说反压可能会导致严重的问题。
这是因为 Flink 的 checkpoint 机制,反压还会影响到两项指标: checkpoint 时长和 state 大小。
- 前者是因为 checkpoint barrier 是不会越过普通数据的,数据处理被阻塞也会导致 checkpoint barrier 流经整个数据管道的时长变长,因而 checkpoint 总体时间(End to End Duration)变长。
- 后者是因为为保证 EOS(Exactly-Once-Semantics,准确一次),对于有两个以上输入管道的 Operator,checkpoint barrier 需要对齐(Alignment),接受到较快的输入管道的 barrier 后,它后面数据会被缓存起来但不处理,直到较慢的输入管道的 barrier 也到达,这些被缓存的数据会被放到state 里面,导致 checkpoint 变大。
这两个影响对于生产环境的作业来说是十分危险的,因为 checkpoint 是保证数据一致性的关键,checkpoint 时间变长有可能导致 checkpoint 超时失败,而 state 大小同样可能拖慢 checkpoint 甚至导致 OOM (使用 Heap-based StateBackend)或者物理内存使用超出容器资源(使用 RocksDBStateBackend)的稳定性问题。
因此,我们在生产中要尽量避免出现反压的情况(顺带一提,为了缓解反压给 checkpoint 造成的压力,社区提出了 FLIP-76: Unaligned Checkpoints[4] 来解耦反压和 checkpoint)。
定位反压节点
要解决反压首先要做的是定位到造成反压的节点,这主要有两种办法:
- 通过 Flink Web UI 自带的反压监控面板;
- 通过 Flink Task Metrics。
前者比较容易上手,适合简单分析,后者则提供了更加丰富的信息,适合用于监控系统。因为反压会向上游传导,这两种方式都要求我们从 Source 节点到 Sink 的逐一排查,直到找到造成反压的根源原因[4]。下面分别介绍这两种办法。
反压监控面板
Flink Web UI 的反压监控提供了 SubTask 级别的反压监控,原理是通过周期性对 Task 线程的栈信息采样,得到线程被阻塞在请求 Buffer(意味着被下游队列阻塞)的频率来判断该节点是否处于反压状态。默认配置下,这个频率在 0.1 以下则为 OK,0.1 至 0.5 为 LOW,而超过 0.5 则为 HIGH。
图1. Flink 1.8 的 Web UI 反压面板(来自官方博客)
如果处于反压状态,那么有两种可能性:
- 该节点的发送速率跟不上它的产生数据速率。这一般会发生在一条输入多条输出的 Operator(比如 flatmap)。
- 下游的节点接受速率较慢,通过反压机制限制了该节点的发送速率。
如果是第一种状况,那么该节点则为反压的根源节点,它是从 Source Task 到 Sink Task 的第一个出现反压的节点。如果是第二种情况,则需要继续排查下游节点。
值得注意的是,反压的根源节点并不一定会在反压面板体现出高反压,因为反压面板监控的是发送端,如果某个节点是性能瓶颈并不会导致它本身出现高反压,而是导致它的上游出现高反压。总体来看,如果我们找到第一个出现反压的节点,那么反压根源要么是就这个节点,要么是它紧接着的下游节点。
那么如果区分这两种状态呢?很遗憾只通过反压面板是无法直接判断的,我们还需要结合 Metrics 或者其他监控手段来定位。此外如果作业的节点数很多或者并行度很大,由于要采集所有 Task 的栈信息,反压面板的压力也会很大甚至不可用。
Task Metrics
Flink 提供的 Task Metrics 是更好的反压监控手段,但也要求更加丰富的背景知识。
首先我们简单回顾下 Flink 1.5 以后的网路栈,熟悉的读者可以直接跳过。
TaskManager 传输数据时,不同的 TaskManager 上的两个 Subtask 间通常根据 key 的数量有多个 Channel,这些 Channel 会复用同一个 TaskManager 级别的 TCP 链接,并且共享接收端 Subtask 级别的 Buffer Pool。
在接收端,每个 Channel 在初始阶段会被分配固定数量的 Exclusive Buffer,这些 Buffer 会被用于存储接受到的数据,交给 Operator 使用后再次被释放。Channel 接收端空闲的 Buffer 数量称为 Credit,Credit 会被定时同步给发送端被后者用于决定发送多少个 Buffer 的数据。
在流量较大时,Channel 的 Exclusive Buffer 可能会被写满,此时 Flink 会向 Buffer Pool 申请剩余的 Floating Buffer。这些 Floating Buffer 属于备用 Buffer,哪个 Channel 需要就去哪里。而在 Channel 发送端,一个 Subtask 所有的 Channel 会共享同一个 Buffer Pool,这边就没有区分 Exclusive Buffer 和 Floating Buffer。
图2. Flink Credit-Based 网络
我们在监控反压时会用到的 Metrics 主要和 Channel 接受端的 Buffer 使用率有关,最为有用的是以下几个 Metrics:
其中 inPoolUsage 等于 floatingBuffersUsage 与 exclusiveBuffersUsage 的总和。
分析反压的大致思路是:如果一个 Subtask 的发送端 Buffer 占用率很高,则表明它被下游反压限速了;如果一个 Subtask 的接受端 Buffer 占用很高,则表明它将反压传导至上游。反压情况可以根据以下表格进行对号入座(图片来自官网):
图3. 反压分析表
outPoolUsage 和 inPoolUsage 同为低或同为高分别表明当前 Subtask 正常或处于被下游反压,这应该没有太多疑问。而比较有趣的是当 outPoolUsage 和 inPoolUsage 表现不同时,这可能是出于反压传导的中间状态或者表明该 Subtask 就是反压的根源。
如果一个 Subtask 的 outPoolUsage 是高,通常是被下游 Task 所影响,所以可以排查它本身是反压根源的可能性。如果一个 Subtask 的 outPoolUsage 是低,但其 inPoolUsage 是高,则表明它有可能是反压的根源。因为通常反压会传导至其上游,导致上游某些 Subtask 的 outPoolUsage 为高,我们可以根据这点来进一步判断。值得注意的是,反压有时是短暂的且影响不大,比如来自某个 Channel 的短暂网络延迟或者 TaskManager 的正常 GC,这种情况下我们可以不用处理。
对于 Flink 1.9 及以上版本,除了上述的表格,我们还可以根据 floatingBuffersUsage/exclusiveBuffersUsage 以及其上游 Task 的 outPoolUsage 来进行进一步的分析一个 Subtask 和其上游 Subtask 的数据传输。
图4. Flink 1.9 反压分析表
通常来说,floatingBuffersUsage 为高则表明反压正在传导至上游,而 exclusiveBuffersUsage 则表明了反压是否存在倾斜(floatingBuffersUsage 高、exclusiveBuffersUsage 低为有倾斜,因为少数 channel 占用了大部分的 Floating Buffer)。
至此,我们已经有比较丰富的手段定位反压的根源是出现在哪个节点,但是具体的原因还没有办法找到。另外基于网络的反压 metrics 并不能定位到具体的 Operator,只能定位到 Task。特别是 embarrassingly parallel(易并行)的作业(所有的 Operator 会被放入一个 Task,因此只有一个节点),反压 metrics 则派不上用场。
分析具体原因及处理
定位到反压节点后,分析造成原因的办法和我们分析一个普通程序的性能瓶颈的办法是十分类似的,可能还要更简单一点,因为我们要观察的主要是 Task Thread。
在实践中,很多情况下的反压是由于数据倾斜造成的,这点我们可以通过 Web UI 各个 SubTask 的 Records Sent 和 Record Received 来确认,另外 Checkpoint detail 里不同 SubTask 的 State size 也是一个分析数据倾斜的有用指标。
此外,最常见的问题可能是用户代码的执行效率问题(频繁被阻塞或者性能问题)。最有用的办法就是对 TaskManager 进行 CPU profile,从中我们可以分析到 Task Thread 是否跑满一个 CPU 核:如果是的话要分析 CPU 主要花费在哪些函数里面,比如我们生产环境中就偶尔遇到卡在 Regex 的用户函数(ReDoS);如果不是的话要看 Task Thread 阻塞在哪里,可能是用户函数本身有些同步的调用,可能是 checkpoint 或者 GC 等系统活动导致的暂时系统暂停。
当然,性能分析的结果也可能是正常的,只是作业申请的资源不足而导致了反压,这就通常要求拓展并行度。值得一提的,在未来的版本 Flink 将会直接在 WebUI 提供 JVM 的 CPU 火焰图[5],这将大大简化性能瓶颈的分析。
另外 TaskManager 的内存以及 GC 问题也可能会导致反压,包括 TaskManager JVM 各区内存不合理导致的频繁 Full GC 甚至失联。推荐可以通过给 TaskManager 启用 G1 垃圾回收器来优化 GC,并加上 -XX:+PrintGCDetails 来打印 GC 日志的方式来观察 GC 的问题。
总结
反压是 Flink 应用运维中常见的问题,它不仅意味着性能瓶颈还可能导致作业的不稳定性。定位反压可以从 Web UI 的反压监控面板和 Task Metric 两者入手,前者方便简单分析,后者适合深入挖掘。定位到反压节点后我们可以通过数据分布、CPU Profile 和 GC 指标日志等手段来进一步分析反压背后的具体原因并进行针对性的优化。
本文作者:林小铂
本文为云栖社区原创内容,未经允许不得转载。
如何分析及处理 Flink 反压?的更多相关文章
- flink - 反压
http://wuchong.me/blog/2016/04/26/flink-internals-how-to-handle-backpressure/ https://ci.apache.org/ ...
- Flink 反压 浅入浅出
前言 微信搜[Java3y]关注这个朴实无华的男人,点赞关注是对我最大的支持! 文本已收录至我的GitHub:https://github.com/ZhongFuCheng3y/3y,有300多篇原创 ...
- flink反压的监控
反压在流式系统中是一种非常重要的机制,主要作用是当系统中下游算子的处理速度下降,导致数据处理速率低于数据接入的速率时,通过反向背压的方式让数据接入的速率下降,从而避免大量数据积压在flink系统中,最 ...
- 一文搞懂 Flink 网络流控与反压机制
https://www.jianshu.com/p/2779e73abcb8 看完本文,你能get到以下知识 Flink 流处理为什么需要网络流控? Flink V1.5 版之前网络流控介绍 Flin ...
- [转帖]实时流处理系统反压机制(BackPressure)综述
实时流处理系统反压机制(BackPressure)综述 https://blog.csdn.net/qq_21125183/article/details/80708142 2018-06-15 19 ...
- Flink中接收端反压以及Credit机制 (源码分析)
先上一张图整体了解Flink中的反压 可以看到每个task都会有自己对应的IG(inputgate)对接上游发送过来的数据和RS(resultPatation)对接往下游发送数据, 整个反压机制通 ...
- Flink中发送端反压以及Credit机制(源码分析)
上一篇<Flink接收端反压机制>说到因为Flink每个Task的接收端和发送端是共享一个bufferPool的,形成了天然的反压机制,当Task接收数据的时候,接收端会根据积压的数据量以 ...
- 咱们从头到尾讲一次 Flink 网络流控和反压剖析
本文根据 Apache Flink 系列直播整理而成,由 Apache Flink Contributor.OPPO 大数据平台研发负责人张俊老师分享.主要内容如下: 网络流控的概念与背景 TCP的流 ...
- 1、flink介绍,反压原理
一.flink介绍 Apache Flink是一个分布式大数据处理引擎,可对有界数据流和无界数据流进行有状态计算. 可部署在各种集群环境,对各种大小的数据规模进行快速计算. 1.1.有界数据流和无界 ...
随机推荐
- 【LeetCode】 两数之和 twoSum
两数之和 (简单) 题目描述 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数: 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 例如: 给定 nums = [2,7,11, ...
- JSPDF 原理
Jspdf是一个将html内容生成pdf文件的库,原理是对输入浏览器的文字或二进制图片进行base64编码转换,以pdf中应有的形式组织,最终以data uri scheme, data:applic ...
- Week1 - 169.Majority Element
这周刚开始讲了一点Divide-and-Conquer的算法,于是这周的作业就选择在LeetCode上找分治法相关的题目来做. 169.Majority Element Given an array ...
- Centos 下更改MySQL源数据存放目录(datadir)
MySQL在安装完成之后,其源数据默认存放在 /var/lib/mysql/ 目录下,一般情况下,该目录在根目录下,由于Linux系统默认 根目录所在挂载的磁盘容量有限,随着生产数据的不断产生,该目 ...
- 前端004/React常用UI组件
每天进步一点点〜 Ant Design of React //蚂蚁金服设计平台.需要应用何种类型组件可参考API React + mobx + nornj 开发模式文件说明: [1].A.t.html ...
- python+selenium链接对象操作
对于链接对象常见的操作有:单击.获取链接文字.获取链接地址等: from selenium import webdriverfrom time import sleep driver = webdri ...
- MySQL binlog之数据恢复
一.恢复方案1.数据量不是特别大,可以将mysqldump命令备份的数据使用mysql客户端命令或者source命令完成数据的恢复:2.使用Xtrabackup完成数据库的物理备份恢复,期间需要重启数 ...
- ES6 new Set实现数组去重
使用new Set实现数组去重必须结合for of, 如果使用for循环就实现不了 var arr = new Set([1, 2, 1, 1, 2, 3, 3, 4, 4]); for (var e ...
- Linux安装了mysql 无法远程连接
问题: 本地安装完mysql,无法远程连接 1.检查mysql进程是否启动 ps -ef|grep -i mysql 2.查看端口是否监听 netstat -ntlp 3.查看iptables配置 v ...
- Neo4j的查询语法笔记(二)
cypher是neo4j官网提供的声明式查询语言,非常强大,用它可以完成任意的图谱里面的查询过滤,我们知识图谱的一期项目 基本开发完毕,后面会陆续总结学习一下neo4j相关的知识.今天接着上篇文章来看 ...