Enovy proxy中的資料統計

ServiceMesher發表於2018-12-07
作者:Matt Klein 
譯者:王帥儉 
原文:blog.envoyproxy.io/envoy-stats… 
本文轉載自:ServcieMesher 社群

這是我在Envoy架構系列中的第3篇文章。這篇文章基於以前關於Envoy的執行緒模型熱重啟功能的帖子。如果您還沒有閱讀這些帖子,請先閱讀。 需要指出的是,隨著預演的結束,我們現在可以進入更有趣的話題!

統計概述

到目前為止,Envoy所做的最重要的事情是為分散式系統的可觀測性提供了一個健壯的平臺。這包括統計資料、日誌記錄和分散式跟蹤。這篇文章將集中在統計資料和Envoy是如何實現允許高容量的同時保持卓越效能的。Envoy目前支援三種不同的統計資料:

  • Counter(計數器):只能增加不會減少的無符號整數。 例如,總請求。

  • Gauge(計量):可以同時增加和減少的無符號整數。 例如,目前有效的請求。

  • Timer/hitogram(計時器/直方圖):無符號整數,最終將產生彙總百分位值。Envoy不區分計時器(通常以毫秒為單位)和原始直方圖(可以是任何單位)。 例如,上游請求時間(以毫秒為單位)。

Envoy目前不支援任何浮點統計資料。

Enovy proxy中的資料統計

Envoy生成很多對除錯分散式系統有用的資料!

統計子系統目標

Envoy統計子系統的總體目標如下:

  • 粗略的線性吞吐量:可以與任意數量的工作執行緒一起擴充套件。另一種說法是:在穩定狀態下,使用stats時應該沒有跨執行緒爭用。

  • 在使用熱重啟時,狀態應該在邏輯上保持一致。這意味著即使有兩個Envoy程式在執行,當邏輯上認為是單個程式時,所有計數器、量規和直方圖都應該是一致的。(有關這方面的更多資訊,請參閱熱重啟這篇文章)。

  • 統計資料應該包含在作用域內並作為一個組釋放。作用域是具有公共字首的統計資料的邏輯分組。例如:http.admin.*。這一點很重要,因為Envoy具有動態性。Envoy支援各種管理API,如監聽器發現服務(LDS)和叢集發現服務(CDS) API。為了不耗盡記憶體,Envoy需要清理不再使用的統計資料。

  • 統計範圍應該能夠重疊和正確的引用計數。這意味著如果作用域A使用一個名為foo.bar.baz的屬性,作用域B也使用foo.bar.baz屬性,那麼foo.bar.baz的屬性的引用計數應該是2。這對於熱重啟(兩個程式將在一段時間內寫入相同的統計資料)和動態管理API(在一段時間內,更新的監聽器或叢集將引用與舊監聽器或叢集相同的統計資料)都是必需的。

  • 統計資料子系統應該能夠很好地執行直到資料平面處理開始時才知道的統計資訊。許多統計資料本質上是“固定的”,可以在載入配置或動態API重新配置資料平面時建立(例如,cluster.foo.upstream_rq_5xx)。這些都是低頻事件。其他統計資訊,例如詳細的HTTP響應程式碼度量(例如,cluster.foo.upstream_rq_503),在資料開始流動之前都不知道。使用“動態”的統計資料永遠不會像使用“固定”的統計資料那樣快,但是即使在處理每個核心每秒數千個請求的10次時,效能仍然應該是足夠的。

作為一個整體,上述目標需要一個複雜的系統來滿足。我們現在將深入研究這個系統是如何工作的。

資料架構

Enovy proxy中的資料統計

圖1:高階統計架構,藍色統計資料顯示了一個作用域分組。

圖1顯示了Envoy資料統計子系統的高階架構。它由以下幾個部分組成。

儲存

stat儲存是Envoy內部的一個單例物件,並提供了一個簡單的介面,通過該介面,其餘程式碼可以獲得作用域、計數器、計量和直方圖的控制程式碼。呼叫程式碼負責維護所有建立的作用域的所有權語義。當作用域被銷燬時,所有包含的統計資料的引用計數都會減少1。如果任何統計資料達到0引用計數,它們將被釋放。

統計資料

如前所述,統計資料包括計數器、量規和直方圖。從終端使用者的角度來看,這些介面使用起來非常簡單。例如,計數器和計量都包括inc()dec()方法,而只有計量包括set()方法。程式設計師看不到任何潛在的儲存複雜性。

Flusher

為了獲得高效能,使用原子CPU指令在內部緩衝所有的狀態變化。在可配置的間隔內,所有計數器和計量都被衝到flusher中。注意,在當前的架構中,直方圖值直接傳送到接收器。下面將更詳細地描述這一點。Flusher在main執行緒中執行。

Sink

統計資料接收器是一個介面,它接受通用的統計資料並將其轉換為特定於後端的連線格式。所有接收器都使用TLS,這樣在重新整理輸出時就不會出現爭用。然而,在實踐中,目前只有主線會沖掉計數器和量規。所有執行緒都重新整理直方圖。

目前Envoy只支援TCP和UDP statsd協議。statsd是一種非常簡單但得到廣泛支援的傳輸格式。在未來,很可能會實現其他本地統計資料接收器,如PrometheusWavefrontInfluxDB。還要注意Envoy目前不支援維度或標籤統計。這將在下面的工作部分中進一步討論。

Admin

從操作的角度來看,能夠實時地到達一個節點並轉儲當前狀態是非常有用的。Envoy可以通過/stats管理端點實現此功能。管理端點直接檢視儲存庫以載入所有計數器和計量並列印它們。這個端點目前不輸出任何直方圖資料。這同樣是由於在當前的實現中直方圖值是直接寫入接收器的,因此儲存不知道它們。

直方圖的架構

正如已經多次提到的,Envoy目前不維護程式內直方圖資料。除了開發效率之外,沒有什麼特別的原因;Lyft使用的statsd攝取管道提供了自己的直方圖支援,並希望直方圖值直接傳送到它。因此,直方圖值目前不能通過管理端點檢視。未來我們很可能直接在Envoy內部實現HDR直方圖。這一點將在下面進一步討論。

執行緒本地熱重啟的能力儲存

以上所有的背景都完成了,現在是時候深入到有趣的部分:實踐中是如何工作的?

統計項

Enovy proxy中的資料統計

圖2:共享記憶體中單獨的計數器/計量統計項

正如我們在熱重啟文章中已經討論過的那樣,最終,所有統計資料都儲存在共享記憶體中,以便可以在所有程式中使用它們。圖2顯示了單個stat條目。它由以下幾個部分組成:

  • Name:完全解析的屬性名,例如http.admin.downstream_cx_active。目前限制為128個字元。

  • Value:屬性的當前值。該資料包含量具的當前值和計數器的當前總價值。所有的資料寫操作都使用原子操作,所以它們在多執行緒環境下是安全的。

  • Pending increment:此資料僅供計數器使用。除了值之外,每個增量都是原子式的。之所以這樣做,是因為大多數統計資料接收器想要獲取重新整理之間的增量而不是總數。因此,在沖洗期間計數器是鎖住的。掛起的增量被寫入計數器,然後歸零。

  • Flags:目前只支援標誌used。這表示如果統計資料被寫過,那麼程式碼能夠區分零和從未寫過。Envoy不會重新整理從來沒有使用過的統計資料,以避免壓倒性的統計後端很少使用的統計資料。

  • Ref count:Ref count允許重疊範圍(可能在多個程式中)使用相同的底層統計資料。只有當ref計數為0時,才釋放統計資料記憶體供將來使用。

儲存

Enovy proxy中的資料統計

圖3:執行緒本地熱重啟支援的儲存體系結構

圖3 顯示了Envoy內部使用的執行緒本地stat儲存的設計。這個版本的商店滿足了之前釋出的所有設計目標。現在我們將詳細介紹它的工作原理。

  1. 該儲存是單例儲存,整個Envoy流程都使用它。所有的範圍、計數器和標準引用都是從這個單例中心儲存庫獲得的。(本節將不介紹直方圖,因為目前直方圖不重要,直接重新整理到TLS 統計資料接收器)。

  2. 當執行緒試圖通過作用域獲取計數器或量規時,它首先在作用域TLS快取中按名稱查詢計數器或量規。如果在快取中找到了統計資料,它將立即返回給呼叫者,而不需要任何鎖定。如果沒有找到該屬性,則必須從範圍中央快取中獲取該屬性。

  3. 範圍中央快取通過標準程式範圍內的互斥鎖鎖定(在穩定狀態下,它不應該被高度競爭,因為統計資訊將在範圍TLS快取中找到)。如果在中心快取中找到了統計資料,那麼它將返回到TLS快取,在那裡儲存它以供以後無鎖查詢。如果在中央快取中沒有找到該屬性,則必須從共享記憶體中分配該屬性。

  4. 共享記憶體包含一系列固定的個人統計條目(圖2)。Envoy包含一個非常基本的分配器,搜尋統計條目名稱相同的槽(支援熱重啟和重疊範圍)或一個空位置,選擇初始化槽如果目前空,增加引用計數,並返回它。這是在熱重啟期間跨程式統計資料的工作方式。兩個程式都將從共享記憶體中分配一個統計資料條目槽,但是其中一個程式最終將引用計數增加到兩個(相同的程式在重疊作用域建立期間發生)。如果在共享記憶體中找不到空間,Envoy將增加一個“panic”屬性並返回一個特殊的溢位屬性槽,以便程式可以在降級狀態下繼續執行。一旦一個統計資料槽被分配,它就被包裝在一個程式本地資料結構中,儲存在範圍中心快取中,儲存在範圍TLS快取中,然後最終返回給呼叫者。

  5. 回想一下,stat子系統的目標之一是使作用域安全可刪除。作用域是全域性物件,由主執行緒和單例儲存管理。刪除作用域時,不同執行緒上的作用域TLS快取可能持有對單個統計資料的引用。為了說明這一點,“作用域快取重新整理”事件通過TLS傳送到每個執行緒。執行緒使用執行緒模型文章中描述的類似RCU的行為釋放所有對作用域統計的引用。一旦計數器或表的最後一次引用計數被減少,共享記憶體統計項插槽也被釋放。這是通過在統計資料條目插槽上減少引用計數來完成的。如果這個引用計數現在為零,那麼這個槽就被完全釋放了,並且可以被任何程式用於一個新的狀態。如果前面的描述有點混亂,總結一下:Envoy中的所有統計資料都由兩個引用計數控制。第一個引用計數用於程式內TLS快取的狀態,第二個引用用於多個程式共享的備份狀態入口槽。

回顧一下,讓我們看看上面的設計如何滿足所有的原始目標:

  • 線性吞吐量:在穩定狀態下,所有的統計資料分配都通過作用域TLS快取進行。對於大量的工作執行緒來說這要求不能加鎖。

  • 在熱重新啟動期間邏輯上是一致的:最終,所有同名的資料在共享記憶體中使用相同的備份儲存。這在流程之間建立了邏輯一致性。

  • 統計資料包含在一個作用域內,可以作為一個組釋放,也可以重疊:作用域具有完全獨立的中央快取和TLS快取,以及獨立的每個統計資料引用計數。一個作用域可以被移除,並且它的所有統計資料的引用計數將會減少,並且可能會被釋放。

  • 足夠的動態統計資料效能:通過範圍TLS快取查詢動態統計資料並使用O(1)雜湊表。

未來的工作

雖然Envoystats子系統工作得很好,但是有幾個方面在未來可以改進:

  • 維度/標記狀態: 大多數更新的狀態後端支援維度/標記,而不僅僅是一個扁平的層次名稱空間。在特使統計資料的某些區域中,這是很有用的。短期而言,我們可能會新增全球標記支援,作為支援它的後端(如Prometheus、Wavefront和流感資料庫)的第一步。

  • 執行緒本地原子快取: 在worker數量和吞吐量極高的情況下,單個stat值上的原子爭用將成為一個問題。這可以通過移動到TLS計數器和壓力錶來解決,這些計數器和壓力錶在沖洗之前被聚集到中央儲存中。

  • 內建的HDR直方圖: 由於幾個原因(管理輸出、基於異常值的延遲檢測和沒有內建直方圖支援的接收器),向Envoy新增直接的HDR直方圖支援將非常有用。

  • 額外的靜態接收器: 如前所述,我們希望直接支援更多的後端,如Prometheus、Wavefront、InfluxDB等。幸運的是,接收器介面很簡單,新增新的實現並不困難。

結論

為了滿足上述目標,Envoy的資料統計子系統的設計是新穎的。到目前為止,它在實踐中表現得非常好,對於其他用例來說,擴充套件起來應該相對容易。

程式碼連結

本文中涉及到的一些介面及實現的標頭檔案請參考下面連結:


相關文章