背景:最近在做flink分析任务相关的一些事情,遇到了一些问题和总结了一些排查手段,排查的过程中发现也有其他人遇到了一些类似问题或者其他人遇到我没有遇到的问题,希望可以和大家一起分享和交流。(大家有兴趣也可以加入钉钉社区群,微信没有钉钉群活跃,贴在文末)
项目背景:生产平均20wqps左右,高峰期40w左右,checkpoint关闭(业务目前不需要exactly-once,容许少量丢失,所以避开了很多checkpoint的调优工作),测试环境最初8w qps用作测试,很多问题没有能够显现出来,所以建议在线上稳定版本上新增一些窗口聚合函数的时候还是要压测下,因为业务数据源的数据的复杂或多样性或可能导致数据倾斜,或热点key导致单节点负载过高,影响整体吞吐量
flink版本号:1.10.0 (https://ci.apache.org/projects/flink/flink-docs-release-1.10/)
1.内存配置问题
问题描述: 先看一张之前配置的图,项目启动的时候分配的是8G内存,task启动后观察控制台只有7个多G,剩下的1G去哪里了呢?之前配置的jvm参数:-XX:MaxMetaspaceSize=512m -Xms5000m -Xmx5000m -XX:MaxDirectMemorySize=2000m
这边展示下flink的内存模型:
我们线上配置的taskmanager.memory.process.size=8G,flink会根据taskmanager.memory.process.size推算出其他区域的内存(详细参数见官方文档)
这里我们主要介绍下heap和off-heap:
1.heap:frameWork Heap + Task heap,flink frameWork Heap 默认taskmanager.memory.framework.heap.size = 128M,task Heap就是业务算子所执行的内存,用户可以自行设置大小,也可以设置比例,框架自动算出来;还有中设置方式: -Xmx and -Xms Framework + Task Heap Memory
2.off-heap:Managed Memery+Direct Memory + Meta + overhead,简单说下管理内存,这个也是导致我们少了1G内存的关键。
引用:Managed memory is managed by Flink and is allocated as native memory (off-heap). The following workloads use managed memory:
Streaming jobs can use it for RocksDB state backend.
Batch jobs can use it for sorting, hash tables, caching of intermediate results.
划重点:Managed memory适用的场景只有使用rocksDB作为状态后端和batch job的场景才会使用到,我们这边是实时stream流job,没有checkpoint没有用到rocksdb;官方建议:其他场景下taskmanager.memory.managed.size=0,内存设置为0,默认情况下taskmanager.memory.managed.fraction=0.4,如果没有其他配置的话,占用总内存的40%
修改完配置,让更多的内存去执行任务算子
2.控制台卡顿问题
问题描述:测试环境8个slot,打开控制台不会卡顿;生产120个slot,控制台完全打不开卡死;导致生产上发生问题,无法通过dashboard去排查,每次只能找运维登上机器去排查
这是当时JobManager的dump文件,我本机用的jdk自带的jvisualvm打开的,搜了下其他工具,下载比较麻烦,感觉这个也挺不错的,或者可以直接用jhat命令去生成html文件分析(感觉没有jvisualvm上手)
问题排查:一开始看到concurrentHashMap占用这么大内存,首先想到的是JobManager加内存,发现JM从8G加到16G一点效果都没有,这里要感谢flink运维大大,发现基本上都是latency的引用,然后看了latency指标个数:task乘sub_task乘operator个数,接近指数倍,线上默认设置多得多指标时间60s,60s去拉一次
解决方法:metrics.latency.interval=0 设置为0关闭,这个参数最好在本地调试得时候开启,线上打开可能会导致JM不停的fullgc,JM的工作主要是心跳和协作各个task,不停gc可能会导致整个任务挂掉
3.OkHttp问题排查
问题描述:前面经过一系列算子处理,到最后一步数据下沉,最后一步数据量很小,所以算子并行读度设置的为1,采用OkHttp异步调用;因为采用得是共享slot的方式,sink算子(okhttp调用的)和其他中间层的算子在一台机器上,sink算子的问题大导致中间层算子背压,为什么呢?
问题排查:一开始单纯看backpressure,导致绕了很大一个圈子,排查问题第一步首先要看gc情况(划重点),一开始jstack线程dump去排查,发现几个i/o线程一直在执行,绕完一大圈回来发现这台机器的gc不正常,dump下堆内存发现一堆RequestBody和Reponse信息。翻了okhttp的源码,采用的是线程池+队列的模型,最大线程消耗完了,放在队列排队,导致堆积在老年代,最终不停fullgc。
一开始我们忽略了gc的问题,认为异步调用,对算子速度不会产生影响,上游数据来了,下游直接异步消费直接返回,类似的问题遇到好几个;
4.数据倾斜
问题:问题比较好展现出来,直接在控制台上查看各个算子的数据量就可以查看到。
数据倾斜会造成短板效应,单机负载过高,吞吐量下降,虽然有些情况下数据倾斜不会造成问题,没达到单机的瓶颈,但是数据量上来,迟早会出问题;举个例子,某个产品热销,一天的点击率可能几百万,有些可能只有几个,这个时候数据进来,keyby分区后,几百万数据会流入一台机器,而其他机器的流量可能只有几十,这个时候我们可以怎么做?
常用的方法是,看到最多得两阶段聚合法,画了个简单的图:
先map,在a后面加一个范围内的随机数,第一次集合数据被打散到其他机器上,在map一次去除后缀,进行第二次聚合算出最终数据,这边美团有个文章讲了好几种spark处理数据倾斜的方式,大家可以参考其中的一些方法(https://tech.meituan.com/2016/05/12/spark-tuning-pro.html )
5.最近排查一个CopyOnWriteStateMap.StateEntryMap对象内存泄露问题,在社区和一些其他人交流都没解决,看了好几个人遇到但是都没有解决的方案,今天看了一个发给flink官方的邮件(http://apache-flink-user-mailing-list-archive.2336050.n4.nabble.com/Memory-Leak-in-ProcessingTimeSessionWindow-td20754.html),遇到的问题相似,具体解决了在进行分享
小建议:1.窗口聚合前,大对象尽量转成小对象,如果你只需要大对象里面的几个参数,尽量把大对象转成小对象,网络传输和窗口状态保存的时候都有效果
2.特定场景最好自定义触发器,reduce算子,agge算子只保存聚合结果没什么问题,processWIndowFunction等会保存所有的数据在内存中,直到定时器触发,容易造成OOM
3.还有想不起来了,后期发现问题继续补上。。。
另外在排障过程中发现一些其他人遇到的问题:
1.flink 使用jdk1.8不调整gc策略的话,默认使用UseParallelGC,默认开启AdaptiveSizePolicy策略,很多人因为这个出问题,我贴出来大家看下,我们这边目前设置的是-XX:+UseSerialGC,目前没导致问题,暂时由于其他问题没优化这一块,但是后期个人会在测试采用G1,贴个原文链接(https://blog.csdn.net/u011418571/article/details/105951730)
总结:
自己总结了下一个简单的排查问题流程图(checkpoint disabled),后期经历各个问题逐渐充实决策树,能更快的去定位问题;大多数情况下gc异常由于数据源或者是flink业务代码导致得,数据源:热点数据导致数据倾斜;flink业务代码:逻辑自己没发现问题;
gc没问题,数据也不发生倾斜的情况下,再去看backpressure,去dump线程看哪个算子比较耗时,再去单独优化算子逻辑;直接看backpressure,可能会绕大圈子