最近接手了一个flink作业,另外一个同事断断续续有的没的写了半年的,不着急,也一直没上线,最近突然要上线,扔给我,要调通上线。

现状是:

1.代码跑不动,资源给的不少,但是就是频繁反压。

2.checkpoint经常失败。

3.也是最严重的,跑着跑着,作业就挂了。

接手之后,秉承着代码的业务逻辑是对的原则,开始了调优之旅,后来发现,还是要从最基本的做起,不然都是白扯。总结了如下几条意见,供自己以后反省。

1.遵循一般的编程原则

代码到手之后,业务逻辑部分简直不忍卒读,整个业务逻辑在一个大函数中,多达几百行,而且里面很多的if语句,一个if语句就超过一屏,而且if语句大量嵌套,其他比如大量的string的累加操作等等,其实都是编程风格中的忌讳,因此,一边是对同事的信任,一边是没勇气看这个代码,刚开始从flink的机制开始调优,没有重构代码,导致浪费了不少时间。后来还是鼓足勇气重构代码,该拆分拆分,该合并合并,才发现中间有一个HashMap一直在Put但是没有clear操作,MD,作业不崩溃才怪。

教训:作业调优,先理解清楚业务,再者好好看懂代码,不管多乱,不然有明显的bug都不知道,至于重构,如果时间可以,尽量吧。

2.确定最耗时的部分

因为作业的DAG图包含6个level,首先需要确定哪个层级最耗时。因为在parse的部分频繁反压,也就是后面的一个level会处理跟不上。但当时说的是写redis,以之前的理解,redis

应该很easy就能支持10W的并发写吧,所以虽然flink提示redis sink有问题,但还是将关注点放在了parse上,也是导致了浪费时间。后来才了解到,虽然同事告诉我是写redis,而且

确实也用的jedis来操作,但其实后台是公司自研的一个类redis的库,而且不保存在内存中,磁盘的写入速度哪里能支持那么大的并发,网卡在瞬间往往都打满,而且写操作还是同步阻塞的,这个也同时解释了前两个问题,由于写入的并发太大,导致反压,所以速度跟不上,速度跟不上,所以在每个level都开启了64个并发,加上写的同步阻塞,checkpoint coordinator需要收到将近300个task的ck 确认才能结束,往往任何一个task的缓慢,都导致了ck的timeout而失败。

教训:真正了解你的持久化储存,了解他能支持的最大的并发和合适的并发及其他因素。

3.了解flink的机制

由于checkpoint总是失败,而flink的checkpoint其实是基于RocksDB去做的,并且是异步和增量的,无论如何都觉得不应该那么容易timeout,后来开启了flink日志的debug级别才发现,ck虽然是异步的,但是需要等到上游所有的barrier都到来才会触发自己的流程,往往这个等的过程就达到分钟级,然后每一个level往后传递,就会导致最后level的task在没有

收到barrier的时候,ck以及timeout。当然,根本的原因还是反压,导致处理的一些task处理的很慢,拖累了整个作业,毕竟barrier是和真正的数据是在一个channel中发送的,如果

数据都积压了,barrier当然也发不下去了。

另一个问题就是chain和slotshareing了,默认flink是开启了chain的,整个是没问题的,这样其实多个operator会在一个task里面,减少了数据传输的开销,但是在调优的时候,也就

不太容易看出哪里是性能瓶颈,slotsharing机制是进行资源分配的策略,默认都是default,所有的task会共享一个slot,也不太好找那个task是瓶颈,将不同的task的slotshareing

设置的不一样,就可以在每个slot中是单独的task,更容易分析是哪里出问题了。

教训:flink默认的机制,一般都是OK的,在找问题的时候可以改,但一般不会是问题点。

4.善用性能检测工具

flink作业本身还是java作业,刚开始觉得跑不动的时候,将java的那些自带工具也基本都用了一遍。

jstack,打印出线程栈,到底是哪里操作在耗时。

jmap,打印出对象信息以及dump文件,看看具体是哪个对象在搞事。

jstat,查看各种gc的信息,各个内存区域的使用是否正常等等。

现在流行的框架,越来越多的使用堆外内存,而这部分内存的回收又不容易,发生泄漏很难查找。

教训:其实保持良好的编码规范,内存出问题的可能不是那么大,更多的还是使用stack看看具体是哪里在耗时。

5.监控

flink有自己的metric,但是往往不够,需要在作业中添加自己的metric,看起来就会很直观。flink的debug日志,虽然会有大量的日志,但有些事情还是必须通过日志来观察。

但这次问题的解决,关键点还在于运维同学的报警,警示数据库的tps太高,一番探讨之后,才发现用的不是真正的redis,才找到的问题的真正所在,性能立马提示,也没有了反压。

教训:如果持久化层有监控,包括IO层,这里往往才真正是出问题的地方,至于CPU和内存,反正是在yarn上,资源不够随时添加即可,反而是IO瓶颈,前面再使劲儿,这里

堵住了,一切都是白扯。

这次调优,虽然最终问题解决了,但还是挺灰头土脸的,希望以后有类似的事情,能够有步骤有重点的推进。

10-11 18:37