淺談可觀測架構(gòu)模式
時間:2021-08-19 15:31:02
手機看文章
掃描二維碼
隨時隨地手機看文章
[導(dǎo)讀]可觀測性(Observability)主要是指了解程序內(nèi)部運行情況的能力。我們不希望應(yīng)用發(fā)布上線后,對應(yīng)用的內(nèi)部一無所知。對于我們來說,整個應(yīng)用就是一個黑盒子。即便應(yīng)用出現(xiàn)錯誤或者發(fā)生崩潰,我們也可以得到崩潰前的所有相關(guān)數(shù)據(jù),這也是飛機黑匣子(FlightRecorder)設(shè)計的...
可觀測性( Observability )主要是指了解程序內(nèi)部運行情況的能力。我們不希望應(yīng)用發(fā)布上線后,對應(yīng)用的內(nèi)部一無所知。對于我們來說,整個應(yīng)用就是一個黑盒子。即便應(yīng)用出現(xiàn)錯誤或者發(fā)生崩潰,我們也可以得到崩潰前的所有相關(guān)數(shù)據(jù),這也是飛機黑匣子( Flight Recorder )設(shè)計的出發(fā)點,如 圖1 所示。
圖1?飛行記錄儀之日志、度量和追蹤
目前,關(guān)于可觀測性的架構(gòu)設(shè)計主要涉及三個部分:日志(logging)、度量(Metrics)和追蹤(Tracing)。下面就從這三個方面詳細(xì)闡述可觀測性架構(gòu)的設(shè)計。
Aliware
要想了解系統(tǒng)的運行情況,最簡單的方法就是查看日志。為此,我們創(chuàng)造了非常多的日志框架、工具和系統(tǒng),如日志文件打印、日志文件采集工具、日志分析系統(tǒng)等。但是,在實際運維中,我們不能將所有信息事無巨細(xì)地全部記錄下來,這樣做反而沒有意義。我們需要為日志設(shè)置不同的級別,如 debug、error、info 等,在開發(fā)、測試、生產(chǎn)等不同環(huán)境下開啟不同的日志級別,并保證在系統(tǒng)運行時能夠?qū)崟r調(diào)控這些日志級別。
通常,我們不用考慮日志處理的問題,畢竟日志處理技術(shù)經(jīng)過長時間的發(fā)展,目前已經(jīng)非常成熟,幾乎所有的編程語言都有對應(yīng)的日志框架。目前,云廠商基本上都會提供日志服務(wù),對接非常簡單,或者自行安裝成熟的日志處理系統(tǒng),如 ElasticStack 等。
度量
Aliware
度量不僅包括 CPU 負(fù)載、內(nèi)存使用量等技術(shù)指標(biāo)的度量,還包括非常多的業(yè)務(wù)度量( Business Metrics ),如每分鐘的交易額、每分鐘會員登錄數(shù)等。對于這些業(yè)務(wù)度量參數(shù),我們在做架構(gòu)設(shè)計的時候,需要以參考指標(biāo)的方式全部羅列出來,以便于觀測上線后的數(shù)據(jù),并做出相應(yīng)的業(yè)務(wù)決策。
這里可能會有讀者產(chǎn)生疑問,我們已經(jīng)使用日志記錄了相關(guān)的數(shù)據(jù),數(shù)據(jù)庫中也保存了最終的數(shù)據(jù),為什么還要增加對數(shù)據(jù)的記錄?為了解答這個問題,我們首先看一下如下區(qū)別。
第一,日志記錄的是發(fā)生在某個時間點的事情,其中包含非常多的細(xì)節(jié),可以說是事無巨細(xì)的。
第二,數(shù)據(jù)庫記錄的是當(dāng)前數(shù)據(jù)的最新快照,我們通常不會關(guān)注中間的過程,如電商網(wǎng)站的商品價格可能經(jīng)過多次調(diào)整,但數(shù)據(jù)庫通常只會記錄商品的最新價格。
第三,度量統(tǒng)計的是一個窗口期的聚合數(shù)據(jù),可以是平均值,也可以是累計值。如果是 CPU 負(fù)載,就統(tǒng)計一段時間的平均值;如果是 1 分鐘內(nèi)交易的訂單數(shù),就需要統(tǒng)計累計值。還有一類比較特殊,就是那些沒有時間區(qū)間的情況,如計數(shù)器等,在應(yīng)用啟動后的整個運行期間,它的值會不停地累加,在應(yīng)用重啟后它會被重新計算。
雖然日志可以計算出一些數(shù)據(jù),如訂單數(shù)、訂單金額等,但這里需要考慮數(shù)據(jù)分析的成本和實時性,以更好地實現(xiàn)計算資源、存儲節(jié)約和快速查詢等。而度量統(tǒng)計的是窗口期的數(shù)據(jù),所以不需要再次計算,從而節(jié)約了計算資源;同時也不需要保存窗口期中每一條具體的數(shù)據(jù),因此可以節(jié)約存儲資源;從用戶角度來說,由于數(shù)據(jù)經(jīng)過了窗口期的預(yù)處理,因此查詢響應(yīng)的速度也會更快。
總體來說,度量部分處理的是可觀測性數(shù)據(jù)中的垂直場景。當(dāng)我們更關(guān)注某一窗口期的聚合數(shù)據(jù),同時關(guān)注點主要聚焦于數(shù)據(jù)的趨勢和對比時,度量剛好能夠滿足這類需求。
典型的度量指標(biāo)主要由以下 5 個部分組成:
1)名稱:因為度量指標(biāo)的名稱要表達其代表的意思,所以最好采用命名空間級聯(lián)的方式,可以使用類似域名的“ . ”分隔,或者使用 Prometheus 中采用的“ _”分隔。2)時間點:采集度量的時間點,通常由度量框架自動設(shè)置。3)數(shù)字值:度量值只能為數(shù)字值,不能為字符串等其他值。4)類型:典型的類型分別為計數(shù)器、直方圖、平均比率、計時器、計量表等。5)標(biāo)簽:主要包括一些元信息,如來源服務(wù)器標(biāo)識、應(yīng)用名稱、分組信息、運行環(huán)境等。標(biāo)簽是為了方便后續(xù)的度量查詢和再聚合處理。
當(dāng)以上信息保存到 Prometheus 等度量系統(tǒng)后,我們可以根據(jù)上述結(jié)構(gòu)進行查詢。PromQL 是 Prometheus 提供的度量查詢語言。
最后為大家介紹基于度量系統(tǒng)的一些預(yù)警規(guī)則。預(yù)警規(guī)則非常豐富,下面列舉幾條以方便大家參考:
1)閾值預(yù)警:當(dāng)某一度量指標(biāo)的值低于或高于某一預(yù)設(shè)值時,就會觸發(fā)警報。例如,CPU 的負(fù)載、業(yè)務(wù)上的度量值跌至零,這些都會觸發(fā)預(yù)警。2)同期數(shù)據(jù)對比:在某些場景下,通過絕對值判斷是不能發(fā)現(xiàn)系統(tǒng)問題的,比如,一個電商網(wǎng)站每天不同時段的交易額是有差別的,所以比對每周同一天同一時段的數(shù)據(jù)來判斷問題會更加精確。3)趨勢預(yù)警:主要是針對計數(shù)器類型設(shè)置的預(yù)警,如果度量數(shù)值出現(xiàn)激增或驟降,或者游離在正常的曲線趨勢之外,就需要引起我們的注意。
回到實際的應(yīng)用開發(fā),大多數(shù)云廠商也提供了度量集成化服務(wù),如阿里云的Prometheus 服務(wù)。在程序中,我們基本上只需要直接對接即可,諸如度量指標(biāo)的采集、存儲、監(jiān)控、告警、圖表展現(xiàn)等數(shù)據(jù)監(jiān)控服務(wù)。
Aliware
微服務(wù)架構(gòu)后基本上是分布式的架構(gòu)設(shè)計。一個簡單的 HTTP 請求可能涉及 5 個以上應(yīng)用,一旦出現(xiàn)問題,就會很難快速定位。例如,用戶反饋會員登錄非常慢,基本要花費 5 秒以上的時間,這種情況該如何定位問題所在?定位問題涉及登錄的 Web應(yīng)用、賬號驗證服務(wù)、會員信息服務(wù)、登錄的安全監(jiān)控系統(tǒng),還涉及 Redis、數(shù)據(jù)庫等。如果沒有一個高效的追蹤系統(tǒng),排查定位問題的復(fù)雜度可想而知。
首先,讓我們看一下追蹤系統(tǒng)的基本元素。
1. ?traceIdtraceId 用來標(biāo)識一個追蹤鏈,如一個 64 位或 128 位長度的字符串。不同追蹤鏈的 traceId 不同。但在某一個追蹤鏈中,traceId 始終保持不變。traceId 通常在請求的入口處生成。如對于 HTTP 請求,traceId 基本上在網(wǎng)關(guān)層生成,也可以延后到具體的 Web 應(yīng)用中生成。在產(chǎn)品環(huán)境中,并不是所有的請求都要啟動追蹤。我們只會采樣部分請求,如只會追蹤 2% 的請求,這樣做主要是考慮到追蹤對整個系統(tǒng)會造成額外的開銷。當(dāng)然,在測試環(huán)境中,為便于排查問題,建議所有請求都開啟追蹤。
2. ?spanIdspanId 用于在一個追蹤鏈中記錄一個跨時間段的操作。例如,我們訪問數(shù)據(jù)庫或者進行 RPC 調(diào)用的過程,就對應(yīng)于一個 spanId。在一個區(qū)間(span)中,ID 的作用是便于識別。ID 通常是一個 64 位的 long 型數(shù)值,名稱的作用是便于用戶了解是什么操作,起始時間和結(jié)束時間的作用是便于了解操作時長。另外,區(qū)間還可以包含其他元信息。總的來說,一個追蹤鏈?zhǔn)怯啥鄠€區(qū)間組成的。區(qū)間提供具體的操作信息。區(qū)間的生成會涉及應(yīng)用中的代碼,我們稱之為區(qū)間的埋點。
3. ?parentId在追蹤鏈中,我們可能需要對一些區(qū)間進行分組,如將某一應(yīng)用內(nèi)部的多個區(qū)間歸在一起,這樣就可以了解該應(yīng)用在整個調(diào)用鏈中消耗的時間。其解決方案是為區(qū)間添加parentId,將不同類別的區(qū)間歸在一起。通常,我們在進入一個應(yīng)用時會進行 parentId 的設(shè)置。例如,進入會員登錄應(yīng)用時會設(shè)置一個 parentId,在進入賬號驗證服務(wù)時會設(shè)置一個 parentId ,這樣我們就能根據(jù)不同的應(yīng)用對區(qū)間進行歸類。在同一個應(yīng)用內(nèi)部,我們還可以基于應(yīng)用的 parentId 設(shè)置子 parentId。如果想要歸類數(shù)據(jù)庫相關(guān)的操作,則將操作全部列在數(shù)據(jù)庫的 parentId 下。
追蹤鏈可以將整個請求在不同應(yīng)用和系統(tǒng)中的操作信息串聯(lián)起來。我們只要輸入traceId ,就可以在追蹤系統(tǒng)中了解整個調(diào)用鏈的詳細(xì)信息。那么,在不同的應(yīng)用和系統(tǒng)中,路徑和區(qū)間信息又是如何采集的呢?Zipkin 是一款知名的路徑跟蹤產(chǎn)品,其中 Brave SDK 可以實現(xiàn)路徑和區(qū)間信息的采集。Brave SDK 負(fù)責(zé)創(chuàng)建路徑和區(qū)間,同時將這些信息異步上報給 Zipkin,完成追蹤鏈的數(shù)據(jù)采集工作。由于路徑和區(qū)間信息的采集是通過遠(yuǎn)程調(diào)用實現(xiàn)的,因此這個采集過程一定要是異步實現(xiàn)的,只有這樣,才能確保不會影響到正常的業(yè)務(wù)操作。最典型的采集方法就是對接 gRPC、Kafka 和 RSocket 等異步協(xié)議或系統(tǒng),以確保數(shù)據(jù)的采集全部是異步的。
事件流訂閱
Aliware
日志記錄、度量和路徑追蹤是實現(xiàn)可觀測性架構(gòu)模式的三大保證。但是在一些場景中,還存在其他非常精巧的設(shè)計,如 Java 飛行記錄器(Java Flight Recorder,JFR)。與前三者不太一樣的是,這是一種基于事件流(Event ?Stream)的推送設(shè)計。我們可以在應(yīng)用中定義各種 JFR 事件,并在業(yè)務(wù)流程中觸發(fā)這些事件。與日志記錄不太一樣的是,JFR 事件可能并不需要像 CPU 負(fù)荷那樣被持久化記錄下來并保存到日志文件中,而是在用戶對這些事件感興趣時才通過訂閱來開啟這些事件的采集,我們暫且稱之為事件流訂閱。與日志分析相比,這種方式更靈活,隨時開啟、隨時分析、隨時退出,而且完全是實時的。
基于 JFR 開啟實時事件流訂閱的好處是,我們不需要關(guān)心額外的開銷對系統(tǒng)性能的影響,因為 JFR 的設(shè)計對系統(tǒng)額外開銷的影響已經(jīng)降到了非常低,只有不到 1%,比日志對系統(tǒng)的影響還要小。這就意味著在生產(chǎn)環(huán)境中,我們可以隨時快速開啟事件流監(jiān)控。
在 Java14 中,JFR 有了進一步的提升和改進,包括性能優(yōu)化、自定義事件 API 和流式訂閱等,這些都使得 JFR 的使用變得更加容易。在最新的 JDK15 中,JFR 的事件類型數(shù)量高達157個,如 CPU 負(fù)載(jdk.CPULoad)、Thread 啟動(jdk.ThreadStart)、文件讀?。╦dk. FileRead)、Socket 讀?。╦dk.SocketRead)等。這些都有事件記錄,對監(jiān)控的幫助也非常大。但 JFR 只針對 Java 平臺,如果某個項目是基于 Java 的,那么 JFR 就可以很好地提升系統(tǒng)的可觀測性。最新的 JUnit5.7 版本也已經(jīng)默認(rèn)支持 JFR 的特性。

目前,關(guān)于可觀測性的架構(gòu)設(shè)計主要涉及三個部分:日志(logging)、度量(Metrics)和追蹤(Tracing)。下面就從這三個方面詳細(xì)闡述可觀測性架構(gòu)的設(shè)計。
日志
Aliware
要想了解系統(tǒng)的運行情況,最簡單的方法就是查看日志。為此,我們創(chuàng)造了非常多的日志框架、工具和系統(tǒng),如日志文件打印、日志文件采集工具、日志分析系統(tǒng)等。但是,在實際運維中,我們不能將所有信息事無巨細(xì)地全部記錄下來,這樣做反而沒有意義。我們需要為日志設(shè)置不同的級別,如 debug、error、info 等,在開發(fā)、測試、生產(chǎn)等不同環(huán)境下開啟不同的日志級別,并保證在系統(tǒng)運行時能夠?qū)崟r調(diào)控這些日志級別。
通常,我們不用考慮日志處理的問題,畢竟日志處理技術(shù)經(jīng)過長時間的發(fā)展,目前已經(jīng)非常成熟,幾乎所有的編程語言都有對應(yīng)的日志框架。目前,云廠商基本上都會提供日志服務(wù),對接非常簡單,或者自行安裝成熟的日志處理系統(tǒng),如 ElasticStack 等。
度量
Aliware
度量不僅包括 CPU 負(fù)載、內(nèi)存使用量等技術(shù)指標(biāo)的度量,還包括非常多的業(yè)務(wù)度量( Business Metrics ),如每分鐘的交易額、每分鐘會員登錄數(shù)等。對于這些業(yè)務(wù)度量參數(shù),我們在做架構(gòu)設(shè)計的時候,需要以參考指標(biāo)的方式全部羅列出來,以便于觀測上線后的數(shù)據(jù),并做出相應(yīng)的業(yè)務(wù)決策。
這里可能會有讀者產(chǎn)生疑問,我們已經(jīng)使用日志記錄了相關(guān)的數(shù)據(jù),數(shù)據(jù)庫中也保存了最終的數(shù)據(jù),為什么還要增加對數(shù)據(jù)的記錄?為了解答這個問題,我們首先看一下如下區(qū)別。
第一,日志記錄的是發(fā)生在某個時間點的事情,其中包含非常多的細(xì)節(jié),可以說是事無巨細(xì)的。
第二,數(shù)據(jù)庫記錄的是當(dāng)前數(shù)據(jù)的最新快照,我們通常不會關(guān)注中間的過程,如電商網(wǎng)站的商品價格可能經(jīng)過多次調(diào)整,但數(shù)據(jù)庫通常只會記錄商品的最新價格。
第三,度量統(tǒng)計的是一個窗口期的聚合數(shù)據(jù),可以是平均值,也可以是累計值。如果是 CPU 負(fù)載,就統(tǒng)計一段時間的平均值;如果是 1 分鐘內(nèi)交易的訂單數(shù),就需要統(tǒng)計累計值。還有一類比較特殊,就是那些沒有時間區(qū)間的情況,如計數(shù)器等,在應(yīng)用啟動后的整個運行期間,它的值會不停地累加,在應(yīng)用重啟后它會被重新計算。
雖然日志可以計算出一些數(shù)據(jù),如訂單數(shù)、訂單金額等,但這里需要考慮數(shù)據(jù)分析的成本和實時性,以更好地實現(xiàn)計算資源、存儲節(jié)約和快速查詢等。而度量統(tǒng)計的是窗口期的數(shù)據(jù),所以不需要再次計算,從而節(jié)約了計算資源;同時也不需要保存窗口期中每一條具體的數(shù)據(jù),因此可以節(jié)約存儲資源;從用戶角度來說,由于數(shù)據(jù)經(jīng)過了窗口期的預(yù)處理,因此查詢響應(yīng)的速度也會更快。
總體來說,度量部分處理的是可觀測性數(shù)據(jù)中的垂直場景。當(dāng)我們更關(guān)注某一窗口期的聚合數(shù)據(jù),同時關(guān)注點主要聚焦于數(shù)據(jù)的趨勢和對比時,度量剛好能夠滿足這類需求。
典型的度量指標(biāo)主要由以下 5 個部分組成:
1)名稱:因為度量指標(biāo)的名稱要表達其代表的意思,所以最好采用命名空間級聯(lián)的方式,可以使用類似域名的“ . ”分隔,或者使用 Prometheus 中采用的“ _”分隔。2)時間點:采集度量的時間點,通常由度量框架自動設(shè)置。3)數(shù)字值:度量值只能為數(shù)字值,不能為字符串等其他值。4)類型:典型的類型分別為計數(shù)器、直方圖、平均比率、計時器、計量表等。5)標(biāo)簽:主要包括一些元信息,如來源服務(wù)器標(biāo)識、應(yīng)用名稱、分組信息、運行環(huán)境等。標(biāo)簽是為了方便后續(xù)的度量查詢和再聚合處理。
當(dāng)以上信息保存到 Prometheus 等度量系統(tǒng)后,我們可以根據(jù)上述結(jié)構(gòu)進行查詢。PromQL 是 Prometheus 提供的度量查詢語言。
最后為大家介紹基于度量系統(tǒng)的一些預(yù)警規(guī)則。預(yù)警規(guī)則非常豐富,下面列舉幾條以方便大家參考:
1)閾值預(yù)警:當(dāng)某一度量指標(biāo)的值低于或高于某一預(yù)設(shè)值時,就會觸發(fā)警報。例如,CPU 的負(fù)載、業(yè)務(wù)上的度量值跌至零,這些都會觸發(fā)預(yù)警。2)同期數(shù)據(jù)對比:在某些場景下,通過絕對值判斷是不能發(fā)現(xiàn)系統(tǒng)問題的,比如,一個電商網(wǎng)站每天不同時段的交易額是有差別的,所以比對每周同一天同一時段的數(shù)據(jù)來判斷問題會更加精確。3)趨勢預(yù)警:主要是針對計數(shù)器類型設(shè)置的預(yù)警,如果度量數(shù)值出現(xiàn)激增或驟降,或者游離在正常的曲線趨勢之外,就需要引起我們的注意。
回到實際的應(yīng)用開發(fā),大多數(shù)云廠商也提供了度量集成化服務(wù),如阿里云的Prometheus 服務(wù)。在程序中,我們基本上只需要直接對接即可,諸如度量指標(biāo)的采集、存儲、監(jiān)控、告警、圖表展現(xiàn)等數(shù)據(jù)監(jiān)控服務(wù)。
追蹤
Aliware
微服務(wù)架構(gòu)后基本上是分布式的架構(gòu)設(shè)計。一個簡單的 HTTP 請求可能涉及 5 個以上應(yīng)用,一旦出現(xiàn)問題,就會很難快速定位。例如,用戶反饋會員登錄非常慢,基本要花費 5 秒以上的時間,這種情況該如何定位問題所在?定位問題涉及登錄的 Web應(yīng)用、賬號驗證服務(wù)、會員信息服務(wù)、登錄的安全監(jiān)控系統(tǒng),還涉及 Redis、數(shù)據(jù)庫等。如果沒有一個高效的追蹤系統(tǒng),排查定位問題的復(fù)雜度可想而知。
首先,讓我們看一下追蹤系統(tǒng)的基本元素。
1. ?traceIdtraceId 用來標(biāo)識一個追蹤鏈,如一個 64 位或 128 位長度的字符串。不同追蹤鏈的 traceId 不同。但在某一個追蹤鏈中,traceId 始終保持不變。traceId 通常在請求的入口處生成。如對于 HTTP 請求,traceId 基本上在網(wǎng)關(guān)層生成,也可以延后到具體的 Web 應(yīng)用中生成。在產(chǎn)品環(huán)境中,并不是所有的請求都要啟動追蹤。我們只會采樣部分請求,如只會追蹤 2% 的請求,這樣做主要是考慮到追蹤對整個系統(tǒng)會造成額外的開銷。當(dāng)然,在測試環(huán)境中,為便于排查問題,建議所有請求都開啟追蹤。
2. ?spanIdspanId 用于在一個追蹤鏈中記錄一個跨時間段的操作。例如,我們訪問數(shù)據(jù)庫或者進行 RPC 調(diào)用的過程,就對應(yīng)于一個 spanId。在一個區(qū)間(span)中,ID 的作用是便于識別。ID 通常是一個 64 位的 long 型數(shù)值,名稱的作用是便于用戶了解是什么操作,起始時間和結(jié)束時間的作用是便于了解操作時長。另外,區(qū)間還可以包含其他元信息。總的來說,一個追蹤鏈?zhǔn)怯啥鄠€區(qū)間組成的。區(qū)間提供具體的操作信息。區(qū)間的生成會涉及應(yīng)用中的代碼,我們稱之為區(qū)間的埋點。
3. ?parentId在追蹤鏈中,我們可能需要對一些區(qū)間進行分組,如將某一應(yīng)用內(nèi)部的多個區(qū)間歸在一起,這樣就可以了解該應(yīng)用在整個調(diào)用鏈中消耗的時間。其解決方案是為區(qū)間添加parentId,將不同類別的區(qū)間歸在一起。通常,我們在進入一個應(yīng)用時會進行 parentId 的設(shè)置。例如,進入會員登錄應(yīng)用時會設(shè)置一個 parentId,在進入賬號驗證服務(wù)時會設(shè)置一個 parentId ,這樣我們就能根據(jù)不同的應(yīng)用對區(qū)間進行歸類。在同一個應(yīng)用內(nèi)部,我們還可以基于應(yīng)用的 parentId 設(shè)置子 parentId。如果想要歸類數(shù)據(jù)庫相關(guān)的操作,則將操作全部列在數(shù)據(jù)庫的 parentId 下。
追蹤鏈可以將整個請求在不同應(yīng)用和系統(tǒng)中的操作信息串聯(lián)起來。我們只要輸入traceId ,就可以在追蹤系統(tǒng)中了解整個調(diào)用鏈的詳細(xì)信息。那么,在不同的應(yīng)用和系統(tǒng)中,路徑和區(qū)間信息又是如何采集的呢?Zipkin 是一款知名的路徑跟蹤產(chǎn)品,其中 Brave SDK 可以實現(xiàn)路徑和區(qū)間信息的采集。Brave SDK 負(fù)責(zé)創(chuàng)建路徑和區(qū)間,同時將這些信息異步上報給 Zipkin,完成追蹤鏈的數(shù)據(jù)采集工作。由于路徑和區(qū)間信息的采集是通過遠(yuǎn)程調(diào)用實現(xiàn)的,因此這個采集過程一定要是異步實現(xiàn)的,只有這樣,才能確保不會影響到正常的業(yè)務(wù)操作。最典型的采集方法就是對接 gRPC、Kafka 和 RSocket 等異步協(xié)議或系統(tǒng),以確保數(shù)據(jù)的采集全部是異步的。
事件流訂閱
Aliware
日志記錄、度量和路徑追蹤是實現(xiàn)可觀測性架構(gòu)模式的三大保證。但是在一些場景中,還存在其他非常精巧的設(shè)計,如 Java 飛行記錄器(Java Flight Recorder,JFR)。與前三者不太一樣的是,這是一種基于事件流(Event ?Stream)的推送設(shè)計。我們可以在應(yīng)用中定義各種 JFR 事件,并在業(yè)務(wù)流程中觸發(fā)這些事件。與日志記錄不太一樣的是,JFR 事件可能并不需要像 CPU 負(fù)荷那樣被持久化記錄下來并保存到日志文件中,而是在用戶對這些事件感興趣時才通過訂閱來開啟這些事件的采集,我們暫且稱之為事件流訂閱。與日志分析相比,這種方式更靈活,隨時開啟、隨時分析、隨時退出,而且完全是實時的。
基于 JFR 開啟實時事件流訂閱的好處是,我們不需要關(guān)心額外的開銷對系統(tǒng)性能的影響,因為 JFR 的設(shè)計對系統(tǒng)額外開銷的影響已經(jīng)降到了非常低,只有不到 1%,比日志對系統(tǒng)的影響還要小。這就意味著在生產(chǎn)環(huán)境中,我們可以隨時快速開啟事件流監(jiān)控。
在 Java14 中,JFR 有了進一步的提升和改進,包括性能優(yōu)化、自定義事件 API 和流式訂閱等,這些都使得 JFR 的使用變得更加容易。在最新的 JDK15 中,JFR 的事件類型數(shù)量高達157個,如 CPU 負(fù)載(jdk.CPULoad)、Thread 啟動(jdk.ThreadStart)、文件讀?。╦dk. FileRead)、Socket 讀?。╦dk.SocketRead)等。這些都有事件記錄,對監(jiān)控的幫助也非常大。但 JFR 只針對 Java 平臺,如果某個項目是基于 Java 的,那么 JFR 就可以很好地提升系統(tǒng)的可觀測性。最新的 JUnit5.7 版本也已經(jīng)默認(rèn)支持 JFR 的特性。