做者 | Nico Krube
譯者 | 王強html
在以前的文章中,咱們從高級抽象到底層細節各個層面全面介紹了 Flink 網絡棧的工做機制。做爲這一系列的第二篇文章,本文將在第一篇的基礎上更進一步,主要探討如何監視與網絡相關的指標,從而識別背壓等因素帶來的影響,或找出吞吐量和延遲的瓶頸所在。本文將簡要介紹處理背壓的手段,而以後的文章將進一步研究網絡棧微調的話題。若是你不是很熟悉網絡棧的知識,強烈建議先閱讀本系列的第一篇文章 《原理解析 | 深刻了解 Apache Flink 的網絡協議棧》。java
網絡監控工做中最重要的環節可能就是監控背壓了,所謂背壓是指系統接收數據的速率高於其處理速度 [1]。這種現象將給發送者帶來壓力,而致使它的緣由可能有兩種狀況:apache
這多是由於接收器自己就遇到了背壓,因此沒法以與發送方相同的速率繼續處理數據;也有多是接收器由於垃圾回收工做、缺乏系統資源或 I/O 瓶頸而暫時卡住了。緩存
這種狀況可能和接收器沒有(直接)關係,咱們說這時是發送器遇到了背壓,由於在同一臺機器上運行的全部子任務共享的網絡帶寬可能供不該求了。請注意,除了 Flink 的網絡棧以外可能還有其餘網絡用戶,例如源(source)和匯(sink)、分佈式文件系統(檢查點、網絡附加存儲)、日誌記錄和指標監測等。咱們以前的一篇關於容量規劃的文章(https://www.ververica.com/blog/how-to-size-your-apache-flink-cluster-general-guidelines)介紹了更多相關內容。網絡
[1] 若是你不熟悉背壓,不瞭解它與 Flink 的交互方式,建議閱讀咱們在 2015 年發表的關於背壓的文章(https://www.ververica.com/blog/how-flink-handles-backpressure)。併發
當背壓出現時,它將一路向上遊傳導並最終到達你的源,還會減慢它們的速度。這自己並非一件壞事,只是代表你缺少足夠的資源處理當前的負載。但你可能想要作一些改進,在不動用更多資源的前提下處理更高的負載。爲此你須要找到(1)瓶頸在哪裏(位於哪一個任務 / 操做符)和(2)產生瓶頸的緣由。Flink 提供了兩種識別瓶頸的機制:oracle
Flink 的 Web UI 大概是快速排除故障時的首選,但它存在一些缺點,咱們將在下面解釋。另外一方面,Flink 的網絡指標更適合持續監控和推斷是哪些瓶頸致使了背壓,並分析這些瓶頸的本質屬性。咱們將在下文中具體介紹這兩個部分。在這兩種狀況下,你都須要從全部的源和匯中找出背壓的根源。調查工做的起點通常來講是最後一個承受背壓的操做符;並且最後這個操做符極可能就是背壓產生的源頭。dom
背壓監視器只暴露在 Flink 的 WebUI[2] 中。因爲它是僅在請求時纔會觸發的活動組件,所以目前沒法經過監控指標來提供給用戶。背壓監視器經過 Thread.getStackTrace() 對 TaskManager 上運行的全部任務線程採樣,並計算緩存請求中阻塞任務的樣本數。這些任務之因此會阻塞,要麼是由於它們沒法按照網絡緩衝區生成的速率發送這些緩存,要麼就是下游任務處理它們的速度很慢,沒法保證發送的速率。背壓監視器將顯示阻塞請求與總請求的比率。因爲某些背壓被認爲是正常 / 臨時的,因此監視器將顯示如下狀態:分佈式
雖然說你也能夠調整刷新間隔、樣本數或樣本之間的延遲等參數,但一般狀況下這些參數用不着你來調整,由於默認值提供的結果已經夠好了。ide
[2] 你還能夠經過 REST API 訪問背壓監視器:/jobs/:jobid/vertices/:vertexid/backpressure
背壓監視器能夠幫助你找到背壓源自何處(位於哪一個任務 / 操做符)。但你無法用它進一步推斷背壓產生的緣由。此外,對於較大的做業或較高的並行度來講,背壓監視器顯示的信息就太亂了,很難分析,還可能要花些時間才能完整收集來自 TaskManager 的數據。另請注意,採樣工做可能還會影響你當前做業的性能。
網絡指標和任務 I/O 指標比背壓監視器更輕量一些,並且會針對當前運行的每一個做業不斷更新。咱們能夠利用這些指標得到更多信息,收集到的信息除了用來監測背壓外還有其餘用途。和用戶關係最大的指標有:
警告:爲了完整起見,咱們將簡要介紹 outputQueueLength 和 inputQueueLength 這兩個指標。它們有點像 [out、in] PoolUsage 指標,但這兩個指標分別顯示的是發送方子任務的輸出隊列和接收方子任務的輸入隊列中的緩存數量。但想要推斷緩存的準確數量是很難的,並且本地通道也有一個很微妙的特殊問題:因爲本地輸入通道沒有本身的隊列(它直接使用輸出隊列),所以通道的這個值始終爲 0(參見 FLINK-12576,https://issues.apache.org/jira/browse/FLINK-12576);在只有本地輸入通道的狀況下 inputQueueLength = 0。
總的來講,咱們不鼓勵使用 outputQueueLength 和 inputQueueLength,由於它們的解析很大程度上取決於運算符當前的並行度以及獨佔緩存和浮動緩存的配置數量。相比之下,咱們建議使用各類 *PoolUsage 指標,它們會爲用戶提供更詳盡的信息。
注意:若是你要推斷緩存的使用率,請記住如下幾點:
任何至少使用過一次的傳出通道老是佔用一個緩存(Flink 1.5 及更高版本)。
Flink 1.8 及較早版本:這個緩存(即便是空的!)老是在 backlog 中計 1,所以接收器試圖爲它保留一個浮動緩存區。
Flink 1.9 及以上版本:只有當一個緩存已準備好消費時纔在 backlog 中計數,好比說它已滿或已刷新時(請參閱 FLINK-11082)。
接收器僅在反序列化其中的最後一條記錄後才釋放接收的緩存。
後文會綜合運用這些指標,以瞭解背壓和資源的使用率 / 效率與吞吐量的關係。後面還會有一個獨立的部分具體介紹與延遲相關的指標。
有兩組指標能夠用來監測背壓:它們分別是(本地)緩衝池使用率和輸入 / 輸出隊列長度。這兩種指標的粒度粗細各異,惋惜都不夠全面,怎樣解讀這些指標也有不少說法。因爲隊列長度指標解讀起來有一些先天困難,咱們將重點關注輸入和輸出池的使用率指標,該指標也提供了更多細節信息。
下表總結了全部組合及其解釋。但請記住,背壓多是次要的的或臨時的(也就是無需查看),或者只出如今特定通道上,或是由特定 TaskManager 上的其餘 JVM 進程(例如 GC、同步、I/O、資源短缺等)引發的,源頭不是某個子任務。
outPoolUsage low | outPoolUsage high | |
---|---|---|
inPoolUsage low | 正常 | 注意(產生背壓,當前狀態:上游暫未出現背壓或已經解除背壓) |
inPoolUsage high (Flink 1.9+) | 若是全部上游任務的 outPoolUsage 都很低,則只須要注意(可能最終會產生背壓); 若是任何上游任務的 outPoolUsage 變高,則問題(可能在上游致使背壓,還多是背壓的源頭) | 問題(下游任務或網絡出現背壓,可能會向上遊傳遞) |
咱們甚至能夠經過查看兩個連續任務的子任務的網絡指標來深刻了解背壓產生的緣由:
第一種狀況一般是由於任務正在執行一些應用到全部輸入分區的耗時操做;後者一般是某種誤差的結果,多是數據偏斜或資源可用性 / 分配誤差。後文的「如何處理背壓」一節中會介紹這兩種狀況下的應對措施。
exclusiveBuffersUsage low | exclusiveBuffersUsage high | |
---|---|---|
floatingBuffersUsage low + 全部上游 outPoolUsage low | 正常 | [3] |
floatingBuffersUsage low + 任一上游 outPoolUsage high | 問題(多是網絡瓶頸) | [3] |
floatingBuffersUsage high + 全部上游 outPoolUsage low | 注意(最終只有一些輸入通道出現背壓) | 注意(最終多數或所有輸入通道出現背壓) |
floatingBuffersUsage high + 任一上游 outPoolUsage high | 問題(只有一些輸入通道在承受背壓) | 問題(多數或所有輸入通道都在承受背壓) |
[3] 不該該出現這種狀況
除了上面提到的各個指標的單獨用法外,還有一些組合用法能夠用來探究網絡棧的深層情況:
假設你肯定了背壓的來源,也就是瓶頸所在,下一步就是分析爲何會發生這種狀況。下面咱們按照從基本到複雜的順序列出了致使背壓的一些潛在成因。咱們建議首先檢查基本成因,而後再深刻研究更復雜的成因,不然就可能得出一些錯誤的結論。
另外回想一下,背壓多是暫時的,多是因爲負載高峯、檢查點或做業重啓時數據 backlog 待處理致使的結果。若是背壓是暫時的,那麼忽略它就好了。此外還要記住,分析和解決問題的過程可能會受到瓶頸自己的影響。話雖如此,這裏仍是有幾件事須要檢查一下。
首先,你應該檢查受控機器的基本資源使用狀況,如 CPU、網絡或磁盤 I/O 等指標。若是某些資源在被所有或大量佔用,你能夠執行如下操做:
通常來講,長時間的垃圾回收工做會引起性能問題。你能夠打印 GC 調試日誌(經過 -XX: +PrintGCDetails)或使用某些內存 /GC 分析器來驗證你是否處於這種情況下。因爲 GC 問題的處理與應用程序高度相關,而且獨立於 Flink,所以咱們不會在此詳細介紹(可參考 Oracle 的垃圾收集調整指南,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 瓶頸可能就很難被發現了。例如,48 覈計算機上的單個 CPU 線程瓶頸只會帶來 2%的 CPU 使用率。能夠考慮使用代碼分析器,由於它們能夠顯示每一個線程的 CPU 使用狀況,這樣就能識別出熱線程。
與上面的 CPU/ 線程瓶頸問題相似,共享資源上較高的線程爭用率可能會致使子任務瓶頸。仍是要請出 CPU 分析器,考慮查找用戶代碼中的同步開銷 / 鎖爭用——雖然咱們應該避免在用戶代碼中添加同步性,這可能很危險!還能夠考慮調查共享系統資源。例如,默認 JVM 的 SSL 實現能夠從共享的 /dev/urandom 資源周圍獲取數據。
若是你的瓶頸是由數據誤差引發的,能夠嘗試將數據分區更改成幾個獨立的重鍵,或實現本地 / 預聚合來清除誤差或減輕其影響。
除此以外還有不少狀況。通常來講,爲了削弱瓶頸從而減小背壓,首先要分析它發生的位置,而後找出緣由。最好從檢查哪些資源處於充分利用狀態開始入手。
追蹤各個可能環節出現的延遲是一個獨立的話題。在本節中,咱們將重點關注 Flink 網絡棧中的記錄的等待時間——包括系統網絡鏈接的狀況。在吞吐量較低時,這些延遲會直接受輸出刷新器的緩存超時參數的影響,或間接受任何應用程序代碼延遲的影響。處理記錄的時間比預期的要長或者(多個)計時器同時觸發——並阻止接收器處理傳入的記錄——時,網絡棧內後續記錄的等待時間會大大延長。咱們強烈建議你將本身的指標添加到 Flink 做業中,以便更好地跟蹤做業組件中的延遲,並更全面地瞭解延遲產生的緣由。
Flink 爲追蹤經過系統(用戶代碼以外)的記錄延遲提供了一些支持。但默認狀況下此功能被禁用(緣由參見下文!),必須用 metrics.latency.interval 或 ExecutionConfig #setLatencyTrackingInterval() 在 Flink 的配置中設置延遲追蹤間隔才能啓用此功能。啓用後,Flink 將根據 metrics.latency.granularity 定義的粒度生成延遲直方圖:
這些指標經過特殊的「延遲標記」收集:每一個源子任務將按期發出包含其建立時間戳的特殊記錄。而後,延遲標記與正常記錄一塊兒流動,不會在線路上或緩存隊列中超過正常記錄。可是,延遲標記不會進入應用程序邏輯,並會在那裏超過正常記錄。所以,延遲標記僅測量用戶代碼之間的等待時間,而不是完整的「端到端」延遲。但用戶代碼會間接影響這些等待時間!
因爲 LatencyMarker 就像普通記錄同樣位於網絡緩衝區中,它們也會因緩存已滿而等待,或因緩存超時而刷新。當信道處於高負載時,網絡緩衝區數據不會增長延遲。可是隻要一個信道處於低負載狀態,記錄和延遲標記就會承受最多 buffer_timeout/2 的平均延遲。這個延遲會加到每一個鏈接子任務的網絡鏈接上,在分析子任務的延遲指標時應該考慮這一點。
只要查看每一個子任務暴露的延遲追蹤指標,例如在第 95 百分位,你就應該能識別出是哪些子任務在顯著影響源到匯延遲,而後對其作針對性優化。
本文爲雲棲社區原創內容,未經容許不得轉載。