作者 | Nico Krube 譯者 | 王強
在之前的文章中,我們從高級抽象到底層細節(jié)各個層面全面介紹了 Flink 網(wǎng)絡(luò)棧的工作機制。作為這一系列的第二篇文章,本文將在第一篇的基礎(chǔ)上更進一步,主要探討如何監(jiān)視與網(wǎng)絡(luò)相關(guān)的指標(biāo),從而識別背壓等因素帶來的影響,或找出吞吐量和延遲的瓶頸所在。本文將簡要介紹處理背壓的手段,而之后的文章將進一步研究網(wǎng)絡(luò)棧微調(diào)的話題。如果你不是很熟悉網(wǎng)絡(luò)棧的知識,強烈建議先閱讀本系列的第一篇文章 《原理解析 | 深入了解 Apache Flink 的網(wǎng)絡(luò)協(xié)議棧》。
監(jiān)控
網(wǎng)絡(luò)監(jiān)控工作中最重要的環(huán)節(jié)可能就是監(jiān)控背壓了,所謂背壓是指系統(tǒng)接收數(shù)據(jù)的速率高于其處理速度 [1]。這種現(xiàn)象將給發(fā)送者帶來壓力,而導(dǎo)致它的原因可能有兩種情況:
接收器很慢。
這可能是因為接收器本身就遇到了背壓,所以無法以與發(fā)送方相同的速率繼續(xù)處理數(shù)據(jù);也有可能是接收器因為垃圾回收工作、缺少系統(tǒng)資源或 I/O 瓶頸而暫時卡住了。
網(wǎng)絡(luò)通道很慢。
這種情況可能和接收器沒有(直接)關(guān)系,我們說這時是發(fā)送器遇到了背壓,因為在同一臺機器上運行的所有子任務(wù)共享的網(wǎng)絡(luò)帶寬可能供不應(yīng)求了。請注意,除了 Flink 的網(wǎng)絡(luò)棧之外可能還有其他網(wǎng)絡(luò)用戶,例如源(source)和匯(sink)、分布式文件系統(tǒng)(檢查點、網(wǎng)絡(luò)附加存儲)、日志記錄和指標(biāo)監(jiān)測等。我們之前的一篇關(guān)于容量規(guī)劃的文章(https://www.ververica.com/blog/how-to-size-your-apache-flink-cluster-general-guidelines)介紹了更多相關(guān)內(nèi)容。
[1] 如果你不熟悉背壓,不了解它與 Flink 的交互方式,建議閱讀我們在 2015 年發(fā)表的關(guān)于背壓的文章(https://www.ververica.com/blog/how-flink-handles-backpressure)。
當(dāng)背壓出現(xiàn)時,它將一路向上游傳導(dǎo)并最終到達你的源,還會減慢它們的速度。這本身并不是一件壞事,只是表明你缺乏足夠的資源處理當(dāng)前的負載。但你可能想要做一些改進,在不動用更多資源的前提下處理更高的負載。為此你需要找到(1)瓶頸在哪里(位于哪個任務(wù) / 操作符)和(2)產(chǎn)生瓶頸的原因。Flink 提供了兩種識別瓶頸的機制:
直接通過 Flink 的 Web UI 及其背壓監(jiān)視器識別
間接通過一些網(wǎng)絡(luò)指標(biāo)識別。
Flink 的 Web UI 大概是快速排除故障時的首選,但它存在一些缺點,我們將在下面解釋。另一方面,F(xiàn)link 的網(wǎng)絡(luò)指標(biāo)更適合持續(xù)監(jiān)控和推斷是哪些瓶頸導(dǎo)致了背壓,并分析這些瓶頸的本質(zhì)屬性。我們將在下文中具體介紹這兩個部分。在這兩種情況下,你都需要從所有的源和匯中找出背壓的根源。調(diào)查工作的起點一般來說是最后一個承受背壓的操作符;而且最后這個操作符很可能就是背壓產(chǎn)生的源頭。
背壓監(jiān)視器
背壓監(jiān)視器只暴露在 Flink 的 WebUI[2] 中。由于它是僅在請求時才會觸發(fā)的活動組件,因此目前無法通過監(jiān)控指標(biāo)來提供給用戶。背壓監(jiān)視器通過 Thread.getStackTrace() 對 TaskManager 上運行的所有任務(wù)線程采樣,并計算緩存請求中阻塞任務(wù)的樣本數(shù)。這些任務(wù)之所以會阻塞,要么是因為它們無法按照網(wǎng)絡(luò)緩沖區(qū)生成的速率發(fā)送這些緩存,要么就是下游任務(wù)處理它們的速度很慢,無法保證發(fā)送的速率。背壓監(jiān)視器將顯示阻塞請求與總請求的比率。由于某些背壓被認為是正常 / 臨時的,所以監(jiān)視器將顯示以下狀態(tài):
OK,比率 ≤ 0.10
LOW,0.10 < 比率 ≤ 0.5
HIGH,0.5 < 比率 ≤ 1
雖說你也可以調(diào)整刷新間隔、樣本數(shù)或樣本之間的延遲等參數(shù),但通常情況下這些參數(shù)用不著你來調(diào)整,因為默認值提供的結(jié)果已經(jīng)夠好了。

[2] 你還可以通過 REST API 訪問背壓監(jiān)視器:/jobs/:jobid/vertices/:vertexid/backpressure
背壓監(jiān)視器可以幫助你找到背壓源自何處(位于哪個任務(wù) / 操作符)。但你沒法用它進一步推斷背壓產(chǎn)生的原因。此外,對于較大的作業(yè)或較高的并行度來說,背壓監(jiān)視器顯示的信息就太亂了,很難分析,還可能要花些時間才能完整收集來自 TaskManager 的數(shù)據(jù)。另請注意,采樣工作可能還會影響你當(dāng)前作業(yè)的性能。
網(wǎng)絡(luò)指標(biāo)
網(wǎng)絡(luò)指標(biāo)和任務(wù) I/O 指標(biāo)比背壓監(jiān)視器更輕量一些,而且會針對當(dāng)前運行的每個作業(yè)不斷更新。我們可以利用這些指標(biāo)獲得更多信息,收集到的信息除了用來監(jiān)測背壓外還有其他用途。和用戶關(guān)系最大的指標(biāo)有:
Flink 1.8 及更早版本:outPoolUsage、inPoolUsage。它們是對各個本地緩沖池中已用緩存與可用緩存的比率估計。在使用基于信用的流控制解析 Flink 1.5-1.8 中的 inPoolUsage 時,請注意它只與浮動緩存有關(guān)(獨占緩存不算在緩沖池里)。
Flink 1.9 及更新版本:outPoolUsage、inPoolUsage、floatingBuffersUsage、exclusiveBuffersUsage 它們是對各個本地緩沖池中已用緩存與可用緩存的比率估計。從 Flink 1.9 開始,inPoolUsage 是 floatingBuffersUsage 和 exclusiveBuffersUsage 的總和。
numRecordsOut、numRecordsIn。這兩個指標(biāo)都帶有兩個作用域:一個是運算符,另一個是子任務(wù)。網(wǎng)絡(luò)監(jiān)視使用的是子任務(wù)作用域指標(biāo),并顯示它已發(fā)送 / 接收的記錄總數(shù)。你可能需要進一步研究這些數(shù)字來找出特定時間跨度內(nèi)的記錄數(shù)量,或使用等效的 PerSecond 指標(biāo)。
numBytesOut、numBytesInLocal、numBytesInRemote。表示這個子任務(wù)從本地 / 遠程源發(fā)出或讀取的字節(jié)總數(shù)。也可以通過 PerSecond 指標(biāo)獲取。
numBuffersOut、numBuffersInLocal、numBuffersInRemote。與 numBytes 類似,但這里計算的是網(wǎng)絡(luò)緩沖區(qū)的數(shù)量。
警告:為了完整起見,我們將簡要介紹 outputQueueLength 和 inputQueueLength 這兩個指標(biāo)。它們有點像 [out、in] PoolUsage 指標(biāo),但這兩個指標(biāo)分別顯示的是發(fā)送方子任務(wù)的輸出隊列和接收方子任務(wù)的輸入隊列中的緩存數(shù)量。但想要推斷緩存的準(zhǔn)確數(shù)量是很難的,而且本地通道也有一個很微妙的特殊問題:由于本地輸入通道沒有自己的隊列(它直接使用輸出隊列),因此通道的這個值始終為 0(參見 FLINK-12576,https://issues.apache.org/jira/browse/FLINK-12576);在只有本地輸入通道的情況下 inputQueueLength = 0。
總的來說,我們不鼓勵使用 outputQueueLength 和 inputQueueLength,因為它們的解析很大程度上取決于運算符當(dāng)前的并行度以及獨占緩存和浮動緩存的配置數(shù)量。相比之下,我們建議使用各種 *PoolUsage 指標(biāo),它們會為用戶提供更詳盡的信息。
注意:如果你要推斷緩存的使用率,請記住以下幾點: 任何至少使用過一次的傳出通道總是占用一個緩存(Flink 1.5 及更高版本)。 Flink 1.8 及較早版本:這個緩存(即使是空的?。┛偸窃?backlog 中計 1,因此接收器試圖為它保留一個浮動緩存區(qū)。 Flink 1.9 及以上版本:只有當(dāng)一個緩存已準(zhǔn)備好消費時才在 backlog 中計數(shù),比如說它已滿或已刷新時(請參閱 FLINK-11082)。 接收器僅在反序列化其中的最后一條記錄后才釋放接收的緩存。
后文會綜合運用這些指標(biāo),以了解背壓和資源的使用率 / 效率與吞吐量的關(guān)系。后面還會有一個獨立的部分具體介紹與延遲相關(guān)的指標(biāo)。
背壓
有兩組指標(biāo)可以用來監(jiān)測背壓:它們分別是(本地)緩沖池使用率和輸入 / 輸出隊列長度。這兩種指標(biāo)的粒度粗細各異,可惜都不夠全面,怎樣解讀這些指標(biāo)也有很多說法。由于隊列長度指標(biāo)解讀起來有一些先天困難,我們將重點關(guān)注輸入和輸出池的使用率指標(biāo),該指標(biāo)也提供了更多細節(jié)信息。
如果一項子任務(wù)的 outPoolUsage 為 100%,則它正在經(jīng)受背壓。子任務(wù)是已經(jīng)阻塞了,還是仍在將記錄寫入網(wǎng)絡(luò)緩沖區(qū),取決于 RecordWriter 當(dāng)前正在寫入的緩存有沒有寫滿。這與背壓監(jiān)視器顯示的結(jié)果是不一樣的!
當(dāng) inPoolUsage 為 100%時表示所有浮動緩存都分配給了通道,背壓最終將傳遞到上游。這些浮動緩存處于以下任一狀態(tài)中:由于一個獨占緩存正被占用(遠程輸入通道一直在嘗試維護 #exclusive buffer 的信用),這些浮動緩存被保留下來供將來在通道上使用;它們?yōu)橐粋€發(fā)送器的 backlog 保留下來等待數(shù)據(jù);它們可能包含數(shù)據(jù)并在輸入通道中排隊;或者它們可能包含數(shù)據(jù)并正由接收器的子任務(wù)讀取(一次一個記錄)。
Flink 1.8 及更早的版本:根據(jù) FLINK-11082(https://issues.apache.org/jira/browse/FLINK-11082),即使在正常情況下 100% 的 inPoolUsage 也很常見。
Flink 1.9 及以上版本:如果 inPoolUsage 持續(xù)在 100%左右,這就是出現(xiàn)上游背壓的強烈信號。
下表總結(jié)了所有組合及其解釋。但請記住,背壓可能是次要的的或臨時的(也就是無需查看),或者只出現(xiàn)在特定通道上,或是由特定 TaskManager 上的其他 JVM 進程(例如 GC、同步、I/O、資源短缺等)引起的,源頭不是某個子任務(wù)。
outPoolUsage lowoutPoolUsage highinPoolUsage low正常注意(產(chǎn)生背壓,當(dāng)前狀態(tài):上游暫未出現(xiàn)背壓或已經(jīng)解除背壓)inPoolUsage high (Flink 1.9+)如果所有上游任務(wù)的 outPoolUsage 都很低,則只需要注意(可能最終會產(chǎn)生背壓); 如果任何上游任務(wù)的 outPoolUsage 變高,則問題(可能在上游導(dǎo)致背壓,還可能是背壓的源頭)問題(下游任務(wù)或網(wǎng)絡(luò)出現(xiàn)背壓,可能會向上游傳遞)
我們甚至可以通過查看兩個連續(xù)任務(wù)的子任務(wù)的網(wǎng)絡(luò)指標(biāo)來深入了解背壓產(chǎn)生的原因:
如果接收器任務(wù)的所有子任務(wù)的 inPoolUsage 值都很低,并且有任一上游子任務(wù)的 outPoolUsage 較高,則可能是網(wǎng)絡(luò)瓶頸導(dǎo)致了背壓。由于網(wǎng)絡(luò)是 TaskManager 的所有子任務(wù)共享的資源,因此瓶頸可能不是直接源自這個子任務(wù),而是來自于各種并發(fā)操作,例如檢查點、其他流、外部連接或同一臺計算機上的其他 TaskManager/ 進程。
背壓也可以由一個任務(wù)的所有并行實例或單個任務(wù)實例引起。
第一種情況通常是因為任務(wù)正在執(zhí)行一些應(yīng)用到所有輸入分區(qū)的耗時操作;后者通常是某種偏差的結(jié)果,可能是數(shù)據(jù)偏斜或資源可用性 / 分配偏差。后文的“如何處理背壓”一節(jié)中會介紹這兩種情況下的應(yīng)對措施。
Flink 1.9 及以上版本
如果 floatingBuffersUsage 沒到 100%,那么就不太可能存在背壓。如果它達到了 100% 且所有上游任務(wù)都在承受背壓,說明這個輸入正在單個、部分或全部輸入通道上承受背壓。你可以使用 exclusiveBuffersUsage 來區(qū)分這三種情況: 假設(shè) floatingBuffersUsage 接近 100%,則 exclusiveBuffersUsage 越高,輸入通道承受的背壓越大。在 exclusiveBuffersUsage 接近 100%的極端情況下,所有通道都在承受背壓。
下表總結(jié)了 exclusiveBuffersUsage、floatingBuffersUsage 和上游任務(wù)的 outPoolUsage 之間的關(guān)系,還比上表多了一個 inPoolUsage = floatingBuffersUsage + exclusiveBuffersUsage:
exclusiveBuffersUsage lowexclusiveBuffersUsage highfloatingBuffersUsage low + 所有上游 outPoolUsage low正常[3]floatingBuffersUsage low + 任一上游 outPoolUsage high問題(可能是網(wǎng)絡(luò)瓶頸)[3]floatingBuffersUsage high + 所有上游 outPoolUsage low注意(最終只有一些輸入通道出現(xiàn)背壓)注意(最終多數(shù)或全部輸入通道出現(xiàn)背壓)floatingBuffersUsage high + 任一上游 outPoolUsage high問題(只有一些輸入通道在承受背壓)問題(多數(shù)或全部輸入通道都在承受背壓)
[3] 不應(yīng)該出現(xiàn)這種情況
資源使用率 / 吞吐量
除了上面提到的各個指標(biāo)的單獨用法外,還有一些組合用法可以用來探究網(wǎng)絡(luò)棧的深層狀況:
吞吐量較低時 outPoolUsage 值卻經(jīng)常接近 100%,但同時所有接收器的 inPoolUsage 都很低,這表明我們的信用通知的往返時間(取決于你的網(wǎng)絡(luò)延遲)太久,導(dǎo)致默認的獨占緩存數(shù)量無法充分利用你的帶寬??梢钥紤]增加每通道緩存參數(shù)或嘗試禁用基于信用的流量控制。
numRecordsOut 和 numBytesOut 這個組合可以用來確定序列化記錄的平均大小,進而幫助你針對峰值場景做容量規(guī)劃。
如果要了解緩存填充率和輸出刷新器的影響,可以考察 numBytesInRemote 與 numBuffersInRemote 的組合。在調(diào)整吞吐量(而不是延遲!)時,較低的緩存填充率可能意味著網(wǎng)絡(luò)效率較低。在這種情況下請考慮增加緩存超時時間。請注意,在 Flink 1.8 和 1.9 中,numBuffersOut 僅在緩存快填滿或某事件停用某緩存(例如一個檢查點屏障)時才會增加,這個動作還可能滯后。還請注意,由于緩存是針對遠程信道的優(yōu)化技術(shù),對本地信道影響有限,因此不需要在本地信道上考察緩存填充率。
你還可以使用 numBytesInLocal 和 numBytesInRemote 的組合區(qū)分本地與遠程流量,但在大多數(shù)情況下沒這個必要。
如何處理背壓?
假設(shè)你確定了背壓的來源,也就是瓶頸所在,下一步就是分析為什么會發(fā)生這種情況。下面我們按照從基本到復(fù)雜的順序列出了導(dǎo)致背壓的一些潛在成因。我們建議首先檢查基本成因,然后再深入研究更復(fù)雜的成因,否則就可能得出一些錯誤的結(jié)論。 另外回想一下,背壓可能是暫時的,可能是由于負載高峰、檢查點或作業(yè)重啟時數(shù)據(jù) backlog 待處理導(dǎo)致的結(jié)果。如果背壓是暫時的,那么忽略它就行了。此外還要記住,分析和解決問題的過程可能會受到瓶頸本身的影響。話雖如此,這里還是有幾件事需要檢查一下。
系統(tǒng)資源
首先,你應(yīng)該檢查受控機器的基本資源使用情況,如 CPU、網(wǎng)絡(luò)或磁盤 I/O 等指標(biāo)。如果某些資源在被全部或大量占用,你可以執(zhí)行以下操作:
嘗試優(yōu)化你的代碼。此時代碼分析器是很有用的。
調(diào)整這項資源的 Flink。
通過增加并行度和 / 或增加群集中的計算機數(shù)量來擴展資源。
垃圾收集
一般來說,長時間的垃圾回收工作會引發(fā)性能問題。你可以打印 GC 調(diào)試日志(通過 -XX: +PrintGCDetails)或使用某些內(nèi)存 /GC 分析器來驗證你是否處于這種狀況下。由于 GC 問題的處理與應(yīng)用程序高度相關(guān),并且獨立于 Flink,因此我們不會在此詳細介紹(可參考 Oracle 的垃圾收集調(diào)整指南,https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/index.html 或 Plumbr 的 Java 垃圾回收手冊,https://plumbr.io/java-garbage-collection-handbook)。
CPU/ 線程瓶頸
如果 CPU 瓶頸來自于一個或幾個線程,而整臺機器的 CPU 使用率仍然相對較低,則 CPU 瓶頸可能就很難被發(fā)現(xiàn)了。例如,48 核計算機上的單個 CPU 線程瓶頸只會帶來 2%的 CPU 使用率??梢钥紤]使用代碼分析器,因為它們可以顯示每個線程的 CPU 使用情況,這樣就能識別出熱線程。
線程爭用
與上面的 CPU/ 線程瓶頸問題類似,共享資源上較高的線程爭用率可能會導(dǎo)致子任務(wù)瓶頸。還是要請出 CPU 分析器,考慮查找用戶代碼中的同步開銷 / 鎖爭用——雖然我們應(yīng)該避免在用戶代碼中添加同步性,這可能很危險!還可以考慮調(diào)查共享系統(tǒng)資源。例如,默認 JVM 的 SSL 實現(xiàn)可以從共享的 /dev/urandom 資源周圍獲取數(shù)據(jù)。
加載不均衡
如果你的瓶頸是由數(shù)據(jù)偏差引起的,可以嘗試將數(shù)據(jù)分區(qū)更改為幾個獨立的重鍵,或?qū)崿F(xiàn)本地 / 預(yù)聚合來清除偏差或減輕其影響。
除此之外還有很多情況。一般來說,為了削弱瓶頸從而減少背壓,首先要分析它發(fā)生的位置,然后找出原因。最好從檢查哪些資源處于充分利用狀態(tài)開始入手。
延遲追蹤
追蹤各個可能環(huán)節(jié)出現(xiàn)的延遲是一個獨立的話題。在本節(jié)中,我們將重點關(guān)注 Flink 網(wǎng)絡(luò)棧中的記錄的等待時間——包括系統(tǒng)網(wǎng)絡(luò)連接的情況。在吞吐量較低時,這些延遲會直接受輸出刷新器的緩存超時參數(shù)的影響,或間接受任何應(yīng)用程序代碼延遲的影響。處理記錄的時間比預(yù)期的要長或者(多個)計時器同時觸發(fā)——并阻止接收器處理傳入的記錄——時,網(wǎng)絡(luò)棧內(nèi)后續(xù)記錄的等待時間會大大延長。我們強烈建議你將自己的指標(biāo)添加到 Flink 作業(yè)中,以便更好地跟蹤作業(yè)組件中的延遲,并更全面地了解延遲產(chǎn)生的原因。
Flink 為追蹤通過系統(tǒng)(用戶代碼之外)的記錄延遲提供了一些支持。但默認情況下此功能被禁用(原因參見下文?。?,必須用 metrics.latency.interval 或 ExecutionConfig #setLatencyTrackingInterval() 在 Flink 的配置中設(shè)置延遲追蹤間隔才能啟用此功能。啟用后,F(xiàn)link 將根據(jù) metrics.latency.granularity 定義的粒度生成延遲直方圖:
single:每個操作符子任務(wù)有一個直方圖
operator(默認值):源任務(wù)和操作符子任務(wù)的每個組合有一個直方圖
subtask:源子任務(wù)和操作符子任務(wù)的每個組合有一個直方圖(并行度翻了兩番!)
這些指標(biāo)通過特殊的“延遲標(biāo)記”收集:每個源子任務(wù)將定期發(fā)出包含其創(chuàng)建時間戳的特殊記錄。然后,延遲標(biāo)記與正常記錄一起流動,不會在線路上或緩存隊列中超過正常記錄。但是,延遲標(biāo)記不會進入應(yīng)用程序邏輯,并會在那里超過正常記錄。因此,延遲標(biāo)記僅測量用戶代碼之間的等待時間,而不是完整的“端到端”延遲。但用戶代碼會間接影響這些等待時間!
由于 LatencyMarker 就像普通記錄一樣位于網(wǎng)絡(luò)緩沖區(qū)中,它們也會因緩存已滿而等待,或因緩存超時而刷新。當(dāng)信道處于高負載時,網(wǎng)絡(luò)緩沖區(qū)數(shù)據(jù)不會增加延遲。但是只要一個信道處于低負載狀態(tài),記錄和延遲標(biāo)記就會承受最多 buffer_timeout/2 的平均延遲。這個延遲會加到每個連接子任務(wù)的網(wǎng)絡(luò)連接上,在分析子任務(wù)的延遲指標(biāo)時應(yīng)該考慮這一點。
只要查看每個子任務(wù)暴露的延遲追蹤指標(biāo),例如在第 95 百分位,你就應(yīng)該能識別出是哪些子任務(wù)在顯著影響源到匯延遲,然后對其做針對性優(yōu)化。
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
電子發(fā)燒友App



















































評論