Enovy proxy中的數據統計

做者:Matt Klein 
譯者:王帥儉 
原文: blog.envoyproxy.io/envoy-stats… 
本文轉載自: ServcieMesher 社區

這是我在Envoy架構系列中的第3篇文章。這篇文章基於之前關於Envoy的線程模型熱重啓功能的帖子。若是您尚未閱讀這些帖子,請先閱讀。 須要指出的是,隨着預演的結束,咱們如今能夠進入更有趣的話題!git

統計概述

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

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

  • Gauge(計量):能夠同時增長和減小的無符號整數。 例如,目前有效的請求。數據庫

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

Envoy目前不支持任何浮點統計數據。緩存

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次時,性能仍然應該是足夠的。

做爲一個總體,上述目標須要一個複雜的系統來知足。咱們如今將深刻研究這個系統是如何工做的。

數據架構

圖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直方圖。這一點將在下面進一步討論。

線程本地熱重啓的能力存儲

以上全部的背景都完成了,如今是時候深刻到有趣的部分:實踐中是如何工做的?

統計項

圖2:共享內存中單獨的計數器/計量統計項

正如咱們在熱重啓文章中已經討論過的那樣,最終,全部統計數據都存儲在共享內存中,以即可以在全部進程中使用它們。圖2顯示了單個stat條目。它由如下幾個部分組成:

  • Name:徹底解析的屬性名,例如http.admin.downstream_cx_active。目前限制爲128個字符。

  • Value:屬性的當前值。該數據包含量具的當前值和計數器的當前總價值。全部的數據寫操做都使用原子操做,因此它們在多線程環境下是安全的。

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

  • Flags:目前只支持標誌used。這表示若是統計數據被寫過,那麼代碼可以區分零和從未寫過。Envoy不會刷新曆來沒有使用過的統計數據,以免壓倒性的統計後端不多使用的統計數據。

  • Ref count:Ref count容許重疊範圍(可能在多個進程中)使用相同的底層統計數據。只有當ref計數爲0時,才釋放統計數據內存供未來使用。

存儲

圖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的數據統計子系統的設計是新穎的。到目前爲止,它在實踐中表現得很是好,對於其餘用例來講,擴展起來應該相對容易。

代碼連接

本文中涉及到的一些接口及實現的頭文件請參考下面連接:

相關文章
相關標籤/搜索