開發與維運

淺談可觀測架構模式

可觀測性( Observability )主要是指了解程序內部運行情況的能力。我們不希望應用發佈上線後,對應用的內部一無所知。對於我們來說,整個應用就是一個黑盒子。即便應用出現錯誤或者發生崩潰,我們也可以得到崩潰前的所有相關數據,這也是飛機黑匣子( Flight Recorder )設計的出發點,如 圖1 所示。

1625130756776-3a6b5961-0a13-4e9c-9164-9d97d949d4f4.png

圖1 飛行記錄儀之日誌、度量和追蹤

目前,關於可觀測性的架構設計主要涉及三個部分:日誌(logging)、度量(Metrics)和追蹤(Tracing)。下面就從這三個方面詳細闡述可觀測性架構的設計。

日誌

要想了解系統的運行情況,最簡單的方法就是查看日誌。為此,我們創造了非常多的日誌框架、工具和系統,如日誌文件打印、日誌文件採集工具、日誌分析系統等。但是,在實際運維中,我們不能將所有信息事無鉅細地全部記錄下來,這樣做反而沒有意義。我們需要為日誌設置不同的級別,如 debug、error、info 等,在開發、測試、生產等不同環境下開啟不同的日誌級別,並保證在系統運行時能夠實時調控這些日誌級別。

通常,我們不用考慮日誌處理的問題,畢竟日誌處理技術經過長時間的發展,目前已經非常成熟,幾乎所有的編程語言都有對應的日誌框架。目前,雲廠商基本上都會提供日誌服務,對接非常簡單,或者自行安裝成熟的日誌處理系統,如 ElasticStack 等。

度量

度量不僅包括 CPU 負載、內存使用量等技術指標的度量,還包括非常多的業務度量( Business Metrics ),如每分鐘的交易額、每分鐘會員登錄數等。對於這些業務度量參數,我們在做架構設計的時候,需要以參考指標的方式全部羅列出來,以便於觀測上線後的數據,並做出相應的業務決策。

這裡可能會有讀者產生疑問,我們已經使用日誌記錄了相關的數據,數據庫中也保存了最終的數據,為什麼還要增加對數據的記錄?為了解答這個問題,我們首先看一下如下區別。

第一,日誌記錄的是發生在某個時間點的事情,其中包含非常多的細節,可以說是事無鉅細的。

第二,數據庫記錄的是當前數據的最新快照,我們通常不會關注中間的過程,如電商網站的商品價格可能經過多次調整,但數據庫通常只會記錄商品的最新價格。

第三,度量統計的是一個窗口期的聚合數據,可以是平均值,也可以是累計值。如果是 CPU 負載,就統計一段時間的平均值;如果是 1 分鐘內交易的訂單數,就需要統計累計值。還有一類比較特殊,就是那些沒有時間區間的情況,如計數器等,在應用啟動後的整個運行期間,它的值會不停地累加,在應用重啟後它會被重新計算。

雖然日誌可以計算出一些數據,如訂單數、訂單金額等,但這裡需要考慮數據分析的成本和實時性,以更好地實現計算資源、存儲節約和快速查詢等。而度量統計的是窗口期的數據,所以不需要再次計算,從而節約了計算資源;同時也不需要保存窗口期中每一條具體的數據,因此可以節約存儲資源;從用戶角度來說,由於數據經過了窗口期的預處理,因此查詢響應的速度也會更快。

總體來說,度量部分處理的是可觀測性數據中的垂直場景。當我們更關注某一窗口期的聚合數據,同時關注點主要聚焦於數據的趨勢和對比時,度量剛好能夠滿足這類需求。

典型的度量指標主要由以下 5 個部分組成:

1)名稱:因為度量指標的名稱要表達其代表的意思,所以最好採用命名空間級聯的方式,可以使用類似域名的“ . ”分隔,或者使用 Prometheus 中採用的“ _”分隔。

2)時間點:採集度量的時間點,通常由度量框架自動設置。

3)數字值:度量值只能為數字值,不能為字符串等其他值。

4)類型:典型的類型分別為計數器、直方圖、平均比率、計時器、計量表等。

5)標籤:主要包括一些元信息,如來源服務器標識、應用名稱、分組信息、運行環境等。標籤是為了方便後續的度量查詢和再聚合處理。

當以上信息保存到 Prometheus 等度量系統後,我們可以根據上述結構進行查詢。PromQL 是 Prometheus 提供的度量查詢語言。

最後為大家介紹基於度量系統的一些預警規則。預警規則非常豐富,下面列舉幾條以方便大家參考:

1)閾值預警:當某一度量指標的值低於或高於某一預設值時,就會觸發警報。例如,CPU 的負載、業務上的度量值跌至零,這些都會觸發預警。

2)同期數據對比:在某些場景下,通過絕對值判斷是不能發現系統問題的,比如,一個電商網站每天不同時段的交易額是有差別的,所以比對每週同一天同一時段的數據來判斷問題會更加精確。

3)趨勢預警:主要是針對計數器類型設置的預警,如果度量數值出現激增或驟降,或者遊離在正常的曲線趨勢之外,就需要引起我們的注意。

回到實際的應用開發,大多數雲廠商也提供了度量集成化服務,如阿里雲的Prometheus 服務。在程序中,我們基本上只需要直接對接即可,諸如度量指標的採集、存儲、監控、告警、圖表展現等數據監控服務。

追蹤

微服務架構後基本上是分佈式的架構設計。一個簡單的 HTTP 請求可能涉及 5 個以上應用,一旦出現問題,就會很難快速定位。例如,用戶反饋會員登錄非常慢,基本要花費 5 秒以上的時間,這種情況該如何定位問題所在?定位問題涉及登錄的 Web應用、賬號驗證服務、會員信息服務、登錄的安全監控系統,還涉及 Redis、數據庫等。如果沒有一個高效的追蹤系統,排查定位問題的複雜度可想而知。

首先,讓我們看一下追蹤系統的基本元素。

1.  traceIdtraceId 用來標識一個追蹤鏈,如一個 64 位或 128 位長度的字符串。不同追蹤鏈的 traceId 不同。但在某一個追蹤鏈中,traceId 始終保持不變。traceId 通常在請求的入口處生成。如對於 HTTP 請求,traceId 基本上在網關層生成,也可以延後到具體的 Web 應用中生成。在產品環境中,並不是所有的請求都要啟動追蹤。我們只會採樣部分請求,如只會追蹤 2% 的請求,這樣做主要是考慮到追蹤對整個系統會造成額外的開銷。當然,在測試環境中,為便於排查問題,建議所有請求都開啟追蹤。

2.  spanIdspanId 用於在一個追蹤鏈中記錄一個跨時間段的操作。例如,我們訪問數據庫或者進行 RPC 調用的過程,就對應於一個 spanId。在一個區間(span)中,ID 的作用是便於識別。ID 通常是一個 64 位的 long 型數值,名稱的作用是便於用戶瞭解是什麼操作,起始時間和結束時間的作用是便於瞭解操作時長。另外,區間還可以包含其他元信息。總的來說,一個追蹤鏈是由多個區間組成的。區間提供具體的操作信息。區間的生成會涉及應用中的代碼,我們稱之為區間的埋點。

3.  parentId在追蹤鏈中,我們可能需要對一些區間進行分組,如將某一應用內部的多個區間歸在一起,這樣就可以瞭解該應用在整個調用鏈中消耗的時間。其解決方案是為區間添加parentId,將不同類別的區間歸在一起。通常,我們在進入一個應用時會進行 parentId 的設置。例如,進入會員登錄應用時會設置一個 parentId,在進入賬號驗證服務時會設置一個 parentId ,這樣我們就能根據不同的應用對區間進行歸類。在同一個應用內部,我們還可以基於應用的 parentId 設置子 parentId。如果想要歸類數據庫相關的操作,則將操作全部列在數據庫的 parentId 下。

追蹤鏈可以將整個請求在不同應用和系統中的操作信息串聯起來。我們只要輸入traceId ,就可以在追蹤系統中瞭解整個調用鏈的詳細信息。那麼,在不同的應用和系統中,路徑和區間信息又是如何採集的呢?Zipkin 是一款知名的路徑跟蹤產品,其中 Brave SDK 可以實現路徑和區間信息的採集。Brave SDK 負責創建路徑和區間,同時將這些信息異步上報給 Zipkin,完成追蹤鏈的數據採集工作。由於路徑和區間信息的採集是通過遠程調用實現的,因此這個採集過程一定要是異步實現的,只有這樣,才能確保不會影響到正常的業務操作。最典型的採集方法就是對接 gRPC、Kafka 和 RSocket 等異步協議或系統,以確保數據的採集全部是異步的。

事件流訂閱

日誌記錄、度量和路徑追蹤是實現可觀測性架構模式的三大保證。但是在一些場景中,還存在其他非常精巧的設計,如 Java 飛行記錄器(Java Flight Recorder,JFR)。與前三者不太一樣的是,這是一種基於事件流(Event  Stream)的推送設計。我們可以在應用中定義各種 JFR 事件,並在業務流程中觸發這些事件。與日誌記錄不太一樣的是,JFR 事件可能並不需要像 CPU 負荷那樣被持久化記錄下來並保存到日誌文件中,而是在用戶對這些事件感興趣時才通過訂閱來開啟這些事件的採集,我們暫且稱之為事件流訂閱。與日誌分析相比,這種方式更靈活,隨時開啟、隨時分析、隨時退出,而且完全是實時的。


基於 JFR 開啟實時事件流訂閱的好處是,我們不需要關心額外的開銷對系統性能的影響,因為 JFR 的設計對系統額外開銷的影響已經降到了非常低,只有不到 1%,比日誌對系統的影響還要小。這就意味著在生產環境中,我們可以隨時快速開啟事件流監控。

在 Java14 中,JFR 有了進一步的提升和改進,包括性能優化、自定義事件 API 和流式訂閱等,這些都使得 JFR 的使用變得更加容易。在最新的 JDK15 中,JFR 的事件類型數量高達157個,如 CPU 負載(jdk.CPULoad)、Thread 啟動(jdk.ThreadStart)、文件讀取(jdk. FileRead)、Socket 讀取(jdk.SocketRead)等。這些都有事件記錄,對監控的幫助也非常大。但 JFR 只針對 Java 平臺,如果某個項目是基於 Java 的,那麼 JFR 就可以很好地提升系統的可觀測性。最新的 JUnit5.7 版本也已經默認支持 JFR 的特性。

《阿里云云原生架構實踐》由阿里雲官方出品,阿里雲智能總裁張建鋒、阿里巴巴首席技術官程立聯袂推薦;從設計原則、模式/反模式、技術選項、設計方法、行業案例等多個維度全面總結阿里云云原生架構的方法論和實踐經驗。


現在開放 5 折限時優惠,可掃描下方二維碼或直接點擊https://item.jd.com/13295448.html購買。


mmexport1624859142685.png

Leave a Reply

Your email address will not be published. Required fields are marked *