最近接手了一個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瓶頸,前面再使勁兒,這里
堵住了,一切都是白扯。
這次調優,雖然最終問題解決了,但還是挺灰頭土臉的,希望以后有類似的事情,能夠有步驟有重點的推進。
