Micrometer是基於JVM的應用程序的metrics 工具庫。它爲最流行的監控系統的儀器客戶端提供了一個簡單的外觀,使您無需鎖定供應商便可測試基於JVM的應用程序代碼。它旨在爲您的指標收集活動增長不多或沒有開銷,同時最大限度地提升指標工做的可移植性。html
Micrometer不是分佈式追蹤系統或事件記錄器。Adrian Cole關於Observationbility 3 Ways的演講在突出這些不一樣類型系統之間的差別方面作得很是出色。java
Micrometer包含一個帶有儀器SPI的核心模塊,一套包含各類監測系統(每一個稱爲註冊表)的模塊和一套測試工具。監控系統有三個重要特徵,重要的是要了解:數據庫
· 編程
Dimensionality。系統是否支持使用標記鍵/值對豐富的metric 標準名稱。若是一個系統不是三維的,它是分層的,這意味着它只支持一個統一的metric 名稱。將Micrometer 標準發佈到分層系統時,Micrometer會將這組標籤/鍵值對變平並將其添加到名稱中。後端
· api
Dimensional緩存 |
Hierarchical服務器 |
Atlas, Datadog, Datadog StatsD, Influx, Prometheus, SignalFx, Telegraf StatsD, Wavefront併發 |
Graphite, Ganglia, JMX, Etsy StatsDapp |
·
Rate aggregation 在這種狀況下,咱們是指在規定的時間間隔內彙集一組樣本。一些監測系統但願某些類型的離散樣本(如計數)在發佈前由應用程序轉換爲費率。有些人但願始終發送累積值。還有一些人對此沒有任何意見。
·
客戶端(Client-side) |
服務器端(Server-side) |
Atlas, Datadog, Influx, Graphite, Ganglia, JMX, all StatsD flavors, SignalFx, Wavefront |
Prometheus |
·
Publishing 一些系統但願在應用程序中隨時檢查應用程序的指標,另外一些系統則但願按期將指標推送給它們。
·
客戶端推送(client pushes) |
服務器檢查(Server polls) |
Atlas, Datadog, Graphite, Ganglia, Influx, JMX, all StatsD flavors, SignalFx, Wavefront |
Prometheus |
從一個監控系統到另外一個監控系統的預期還有其餘更小的變化,好比他們對基本計量單位(特別是時間)的概念和度量標準的規範命名慣例。Micrometer 自定義您的指標以知足這些需求,並在每一個註冊表的基礎上。
一個Meter是用於收集關於您的應用程序的一組度量(咱們個別稱爲度量(metrics))的接口。Micrometer 中的Meters 由一個建立並保存MeterRegistry。每一個支持的監控系統都有一個實現MeterRegistry。註冊表的建立方式因每一個實現而異。
Micrometer 打包內含SimpleMeterRegistry每一個meter 的最新值,不會將數據導出到任何地方。若是您尚未首選的監控系統,則可使用簡單的註冊表開始玩指標:
MeterRegistry registry = new SimpleMeterRegistry();
注意 |
A SimpleMeterRegistry在基於Spring的應用程序中爲你自動裝配。 |
Micrometer 提供了一個CompositeMeterRegistry可添加多個註冊表的程序,容許您將metrics 標準同時發佈到多個監視系統。
CompositeMeterRegistry composite = new CompositeMeterRegistry();
Counter compositeCounter = composite.counter("counter");
compositeCounter.increment(); (1) SimpleMeterRegistry simple = new SimpleMeterRegistry(); composite.add(simple); (2)
compositeCounter.increment(); (3)
一、直到組合中存在註冊表爲止,遞增纔是NOOPd。此時計數器的計數仍然爲0。
二、名爲「計數器」的計數器已註冊到簡單註冊表。
三、簡單的註冊表計數器會與組合中的任何其餘註冊表的計數器一塊兒遞增。
Micrometer提供了一個靜態全局註冊表Metrics.globalRegistry和一組靜態構建器,用於基於此註冊表生成計量表。globalRegistry是一個組合註冊表。
class MyComponent {
Counter featureCounter = Metrics.counter("feature", "region", "test"); (1)
void feature() {
featureCounter.increment();
}
void feature2(String type) {
Metrics.counter("feature.2", "type", type).increment(); (2)
}
}class MyApplication {
void start() {
// wire your monitoring system to global static state
Metrics.addRegistry(new SimpleMeterRegistry()); (3)
}
}
一、只要有可能(特別是儀器性能相當重要),請將Meter實例存儲在字段中,以免在每次使用時查看其名稱/標籤。
二、當標籤須要從本地環境中肯定時,您別無選擇,只能在方法體內構建/查找儀表。查找代價只是單個哈希查找,因此對於大多數用途它均可以接受。
三、在米建立以後添加註冊表是能夠的Metrics.counter(…)。這些儀表將被添加到每一個註冊表中,由於它綁定到全局組合。
Micrometer 包與支持組Meter原語包括:Timer,Counter,Gauge,DistributionSummary,LongTaskTimer,FunctionCounter,FunctionTimer,和TimeGauge。不一樣的meter 類型會產生不一樣數量的時間序列metrics。例如,儘管只有一個metric 標準表明a Gauge,但a Timer度量了計時事件的計數和全部事件計時的總時間。
meter 由其名稱和尺寸惟一標識。咱們能夠互換使用術語尺寸和標籤,而Micrometer 接口Tag僅僅是由於它更短。做爲通常規則,應該能夠將名稱用做樞軸。維度容許將特定的指定指標切片以深刻挖掘並推理數據。這意味着若是隻選擇了名稱,用戶可使用其餘維度和關於所顯示值的理由向下鑽取。
Micrometer 採用命名約定,用'.'分隔單詞。(點).因爲不一樣的監控系統對命名約定有不一樣的建議,某些命名約定可能其實是針對一個系統而非另外一個系統的,因此每一個用於監控系統的Micrometer 實施都會包含一個命名約定,該約定將點符號名稱轉換爲建議的命名約定。另外,這個命名約定實現清理了特定字符的名稱和標籤,這些特殊字符是爲他們設計的監視系統所不容許的。若是您想經過NamingConvention在註冊表中實現和設置註冊表,您能夠覆蓋註冊表的默認命名約定:
registry.config().namingConvention(myCustomNamingConvention);
使用命名約定後,在Micrometer 中註冊的如下計時器在各類監控系統中本質上看起來不錯:
registry.timer("http.server.requests");
一、Prometheus - http_server_requests_duration_seconds
二、Atlas - httpServerRequests
三、Graphite - http.server.requests
四、InfluxDB - http_server_requests
經過遵照Micrometer的點符號慣例,您能夠保證監控系統中metric 標準名稱的最大程度的可移植性。
假設咱們試圖測量http請求的數量和數據庫調用的數量。
推薦的方法
registry.counter("database.calls", "db", "users")
registry.counter("http.requests", "uri", "/api/users")
這個變體提供了足夠的上下文,因此若是隻選擇了這個名字,這個值就能夠被推理出來,而且至少具備潛在的意義。例如,若是咱們選擇,database.calls咱們能夠看到全部數據庫調用的總數。而後咱們能夠進行分組或選擇db進一步深刻分析或對每一個數據庫調用的貢獻進行比較分析。
糟糕的作法
registry.counter("calls",
"class", "database",
"db", "users");
registry.counter("calls",
"class", "http",
"uri", "/api/users");
在這種方法中,若是咱們選擇,calls咱們將得到一個值,該值是對數據庫和咱們的API端點的調用次數的總和。若是沒有進一步的維度挖掘,這個時間序列就沒有用。
通用標記能夠在註冊表級別定義,並添加到報告給監視系統的每一個metric 標準中。這一般用於在主機,實例,區域,堆棧等操做環境上進行維度挖掘。
registry.config().commonTags("stack", "prod", "region", "us-east-1");
registry.config().commonTags(Arrays.asList(Tag.of("stack", "prod"), Tag.of("region", "us-east-1"))); // equivalently
調用commonTags附加其餘通用標籤。
重要 |
若是你在Spring環境中,經過添加一個MeterRegistryCustomizerbean來添加通用標籤,以確保在自動配置儀表活頁夾以前應用通用標籤。 |
標記值必須是非空的。
警告 |
注意來自用戶提供的源的標籤值可能會致使度量標準的基數。您應該始終仔細規範並限制用戶提供的輸入。有時候,緣由是偷偷摸摸的。考慮用於在服務端點上記錄HTTP請求的URI標記。若是咱們不將404的值限制爲像NOT_FOUND這樣的值,那麼度量的維數將隨着每一個找不到的資源而增長。 |
每一個註冊表均可以配置儀表過濾器,使您能夠更好地控制儀表註冊的方式和時間以及它們發出的統計類型。儀表過濾器有三個基本功能:
Deny (拒絕/接受)meters 被註冊。
Transform meter ID(例如更更名稱,添加或刪除標籤,更改描述或基本單位)。
Configure 爲某些電錶類型配置分佈統計。
MeterFilter以編程方式將註冊表的實現添加到註冊表中:
registry.config()
.meterFilter(MeterFilter.ignoreTags("too.much.information"))
.meterFilter(MeterFilter.denyNameStartsWith("jvm"));
Meter 過濾器是按順序應用的,而且轉換或配置儀表的結果是連接的。
接受/拒(accept/deny)絕過濾器的詳細形式是:
new MeterFilter() {
public MeterFilterReply accept(Meter.Id id) {
if(id.getName().contains("test")) {
return MeterFilterReply.DENY;
}
return MeterFilterReply.NEUTRAL;
}
}
MeterFilterReply 有三種可能的狀態:
DENY - 不要讓這個儀表被註冊。當您嘗試在註冊表中註冊計量器而且過濾器返回DENY時,註冊表將返回該計量器的NOOP版本(例如NoopCounter,NoopTimer)。您的代碼能夠繼續與NOOP流量計進行交互,但記錄的任何內容都會當即丟棄,而且花費最小。
NEUTRAL- 若是沒有其餘儀表過濾器返回DENY,則儀表正常註冊。
ACCEPT- 若是過濾器返回ACCEPT,儀表當即註冊,不詢問任何其餘過濾器的接受方法。
MeterFilter 爲拒絕/接受類型過濾器提供了幾種便利的靜態構建器:
accept() - 接受每個米,覆蓋任何後續過濾器的決定。
accept(Predicate<Meter.Id>) - 接受與謂詞匹配的任何meter。
acceptNameStartsWith(String) - 接受每一個meter 的匹配前綴。
deny() - 拒絕每個meter,覆蓋任何後續過濾器的決定。
denyNameStartsWith(String) - 拒絕每個具備匹配前綴的meter 。MeterBinder由Micrometer提供的全部開箱即用的實現都具備通用前綴的名稱,以便在UI中進行簡單的分組可視化,但也可使它們做爲具備前綴的組disable/enable 。例如,您能夠拒絕全部JVM指標MeterFilter.denyNameStartsWith("jvm")
deny(Predicate<Meter.Id>) - 拒絕任何匹配謂詞的meter 。
maximumAllowableMetrics(int) - 在註冊表達到必定數量的meter 後拒絕任何計量器。
maximumAllowableTags(String meterNamePrefix, String tagKey, int maximumTagValues, MeterFilter onMaxReached) - 對由匹配系列產生的標籤數量設置上限。
白名單僅對某組指標進行白名單是監視昂貴系統的一種特別常見的狀況。這能夠經過靜態來實現:
denyUnless(Predicate<Meter.Id>)- 拒絕與謂詞不匹配的全部meters 。
Meter 過濾器按照它們在註冊表中配置的順序進行應用,所以能夠堆棧拒絕/接受(deny/accept)過濾器來實現更復雜的規則:
registry.config()
.meterFilter(MeterFilter.acceptNameStartsWith("http"))
.meterFilter(MeterFilter.deny()); (1)
這經過將兩個濾波器堆疊在一塊兒實現了另外一種白名單形式。此註冊表中只有「http」度量標準存在。
轉換過濾器以下所示:
new MeterFilter() {
public Meter.Id map(Meter.Id id) {
if(id.getName().startsWith("test")) {
return id.withName("extra." + id.getName()).withTag("extra.tag", "value");
}
return id;
}
}
該過濾器將名稱前綴和附加標記有條件地添加到名稱爲「test」的儀表中。
MeterFilter 爲許多常見的轉換案例提供便利建造者:
commonTags(Iterable<Tag>) - 爲全部指標添加一組標籤。爲應用程序名稱,主機,區域等添加通用標記是強烈建議的作法。
ignoreTags(String…) - 從每一個meter丟棄匹配的標籤鍵。當標籤明顯變得過於高基數並開始壓迫您的監控系統或花費太多時,這一點特別有用,但您沒法快速更改全部檢測點。
replaceTagValues(String tagKey, Function<String, String> replacement, String… exceptions) - 根據提供的映射替換全部匹配標籤密鑰的標籤值。這能夠用來經過將標記值的一部分映射到其餘東西來減小標記的總基數。
renameTag(String meterNamePrefix, String fromTagKey, String toTagKey) - 爲每一個以給定前綴開頭的度量重命名標籤密鑰。
Timer並DistributionSummary含有除可經過過濾器進行配置數,總,和最大的基礎一套可選的分佈統計。這些分佈統計信息包括預先計算的百分位數,SLA和直方圖。
new MeterFilter() {
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
if (id.getName().startsWith(prefix)) {
return DistributionStatisticConfig.builder()
.publishPercentiles(0.9, 0.95)
.build()
.merge(config);
}
return config;
}
};
通常來講,你應該建立一個新DistributionStatisticConfig的只有你想要配置的部分,而後merge使用輸入配置。這使您能夠下載註冊表提供的默認分佈統計信息並將多個過濾器連接在一塊兒,每一個過濾器配置一些分佈統計信息的一部分(例如,您可能但願全部http請求都有100ms的SLA,但只有幾個關鍵的百分位直方圖端點)。
MeterFilter 爲如下方面提供便利建設者:
maxExpected(Duration/long) - 管理從計時器或摘要發送的百分位直方圖桶的上限。
minExpected(Duration/long) - 管理從定時器或摘要發送的百分位直方圖桶的下限。
Spring Boot提供基於屬性的過濾器,用於按名稱前綴配置SLA,百分位數和百分位柱狀圖。
Micrometer 知道特定的監控系統是否要求在指標發佈以前發生客戶端速率聚合,或者在服務器端做爲查詢的一部分進行臨時性聚合。它根據監控系統預期的風格累積指標。
並非全部的測量值都被報告或最好被視爲一個速率。例如,量表值和長任務計時器活動任務不是速率。
執行服務器端數學計算的監視系統指望在每一個發佈時間間隔報告絕對值。例如,自應用程序開始以來計數器的全部增量絕對計數均在每一個發佈時間間隔發送。
假設咱們有一個稍微正偏向的隨機遊走,每10毫秒選擇一次計數器。若是咱們在像Prometheus這樣的系統中查看原始計數器值,咱們會看到逐步增長的單調遞增函數(步長的寬度是Prometheus輪詢或抓取數據的時間間隔)。
在某個時間窗口內表示沒有匯率彙總的計數器不多有用,由於該表示是計數器遞增的快速度和服務的壽命的函數。在咱們上面的例子中,在服務重啓時計數器會回落到零。只要新實例(好比生產部署)投入使用,匯率圖就會返回到55左右的值。
若是您已實現零宕機時間部署(例如經過紅黑部署),則您應該可以在費率聚合圖上輕鬆設置最低警報閾值,而沒必要從新啓動服務,致使計數器值驟降。
重要 |
對於大多數生產目的而言,不管是警報,自動化canary 分析等,都是基於匯率數據的自動化。 |
另外一類監控系統:
預計匯率數據。鑑於對大多數生產目的的關鍵洞察力,咱們應該根據利率而不是絕對價值來肯定咱們的決策,這樣的系統受益於沒必要花數學來知足查詢。
只有相對較少的數學運算或沒有數據運算,可讓咱們經過查詢來評估數據。對於這些系統,發佈預先彙總的數據是構建有意義表示的惟一方法。
Micrometer 經過累積當前發佈間隔數據的步長值高效地維護速率數據。當輪詢步長值時(例如發佈時),若是步長值檢測到當前時間間隔已過,則會將當前數據移至「前一個」狀態。以前的狀態是直到下一次當前數據覆蓋它爲止所報告的狀態。下面是當前狀態和先前狀態以及輪詢的交互狀況的示意圖:
輪詢函數返回的值始終是每秒*間隔的速率。若是上面說明的步長值表明計數器的值,咱們能夠說計數器在第一個時間間隔中看到了「每秒0.3個增量」,在第二個時間間隔的任什麼時候間均可以報告給後端。
Micrometer 定時器做爲單獨的測量值追蹤至少一個計數和總時間。假設咱們以10秒爲間隔配置發佈,而且咱們看到了20個請求,每一個請求耗時100毫秒。而後在第一個時間間隔內:
count = 10秒*(20次請求/ 10秒)= 20次請求
totalTime = 10秒*(20 * 100毫秒/ 10秒)= 2秒
該count統計是有意義的站立單獨-它是衡量吞吐量。totalTime表示間隔中全部請求的總延遲。另外:
totalTime / count = 2秒/ 20次請求= 0.1秒/請求= 100毫秒/請求
這是平均延遲的一個有用的度量。當一樣的想法應用到totalAmount和count從分發概要發出,該措施被稱爲分佈平均。平均延遲僅僅是一個分佈總結的分佈平均值(一個計時器)。猶如擎天一些監測系統計算從這些統計分佈平均提供設施和Micrometer 將船totalTime和count做爲單獨統計。其餘像Datadog這樣的內置操做並無,而且Micrometer會計算出分配的平均客戶端併發送它。
運送發佈時間間隔的速率足以推斷任什麼時候間窗口內的速率大於或等於發佈時間間隔。在咱們的例子中,若是一個服務繼續收到20個請求,每一個請求在一個給定的分鐘內每隔10秒間隔須要100ms,那麼咱們能夠說:
千分尺count每隔10秒報告一次「20次請求」 。監控系統簡單地總結這六個10秒的時間間隔,並得出120個請求/分鐘的結論。請注意,這是進行此總和的監控系統,而不是千分尺。
千分尺報告totalTime每隔10秒間隔「2秒」 。監控系統能夠在一分鐘內累計全部總時間統計數據,以在分鐘間隔內產生總時間的「12秒」。而後,平均延遲與咱們預期的同樣:12秒/ 120次請求= 100毫秒/請求。
計數器報告一個指標,一個計數。該Counter接口容許您經過一個固定的量,它必須爲正遞增。
小費 |
永遠不要計算你能夠用一個Timer或一個總結的東西DistributionSummary!雙方Timer並DistributionSummary始終發佈除了其餘的測量事件的計數。 |
當在櫃檯外創建圖表和警報時,一般您應該對測量某個事件在給定時間間隔內發生的速率感興趣。考慮一個簡單的隊列。計數器能夠用來衡量項目被插入和刪除的速度。
人們很容易在第一次懷孕可視化絕對數值,而不是速度,但絕對數量一般是一個既與一些使用快速性的功能和應用程序實例的下儀表的使用壽命。構建儀表盤和每隔一段時間計數器計數率的警報無視應用程序的使用壽命,讓您在應用程序啓動後很長時間內看到異常行爲。
注意 |
請務必在跳轉到使用計數器以前仔細閱讀計時器部分,由於計時器記錄計時事件的計數,做爲進入計時的度量套件的一部分。對於那些你想要的代碼段,你不須要單獨添加一個計數器。 |
下面的代碼模擬一個真實的計數器,它的速率在短期窗內顯示出一些擾動。
Normal rand = ...; // a random generator
MeterRegistry registry = ...
Counter counter = registry.counter("counter"); (1)
Flux.interval(Duration.ofMillis(10))
.doOnEach(d -> {
if (rand.nextDouble() + 0.1 > 0) { (2)
counter.increment(); (3)
}
})
.blockLast();
大多數計數器可使用名稱和可選的一組標籤在註冊表自己以外建立。
稍有積極偏向的隨機遊走。
這就是你如何與櫃檯互動。您也能夠counter.increment(n)在一次操做中調用增長1以上。
在Counter界面自己還有一個流暢的計數器生成器,能夠訪問基本單元和描述等較少使用的選項。您能夠經過撥打電話註冊計數器做爲其結構的最後一步register。
Counter counter = Counter
.builder("counter")
.baseUnit("beans") // optional
.description("a description of what this counter does") // optional
.tags("region", "test") // optional
.register(registry);
Micrometer 還提供了一種更不常用的計數器模式,用於跟蹤單調增長的函數(一個函數保持不變或隨時間增長,但從不減小)。一些監控系統,如Prometheus,將計數器的累計值推送到後端,但其餘監控系統則公佈計數器在推送間隔內遞增的速率。經過採用這種模式,您可讓您的監測系統使用Micrometer 來選擇是否對計數器進行標準化,而且您的計數器能夠在不一樣類型的監測系統中保持可移植性。
Cache cache = ...; // suppose we have a Guava cache with stats recording on
registry.more().counter("evictions", tags, cache, c -> c.stats().evictionCount()); (1)
evictionCount() 是一個單調遞增的函數,隨着從其生命開始時每次緩存逐出逐漸增長。
函數跟蹤計數器與監視系統的速率規範化功能(不管這是查詢語言的工件仍是數據推送到系統的方式)一致,在函數的累積值之上增長了一層豐富性自己。能夠推論的速率在其值在增長,這樣的速度是否在一個可接受的結合,增長或下降隨着時間的推移等。
警告 |
Micrometer 不能保證你的功能的單調性。經過使用這個簽名,你基於你對它的定義的瞭解來斷言它的單調性。 |
在FunctionCounter接口自己還有一個功能計數器的流暢構建器,能夠訪問基本單元和描述等較少使用的選項。您能夠經過撥打電話註冊計數器做爲其結構的最後一步register(MeterRegistry)。
MyCounterState state = ...;
FunctionCounter counter = FunctionCounter
.builder("counter", state, state -> state.count())
.baseUnit("beans") // optional
.description("a description of what this counter does") // optional
.tags("region", "test") // optional
.register(registry);
量表是獲取當前值的句柄。量表的典型示例是集合或映射的大小或處於運行狀態的線程數。
小費 |
儀表對於監視具備天然上限的事物頗有用。咱們不建議使用計量器來監視諸如請求計數之類的事情,由於它們能夠在應用程序實例的生命週期內不受限制地增加。 |
小費 |
決不衡量的東西,你能夠用一個數Counter! |
Micrometer 採起的立場是儀表應該被採樣而不是設置,因此沒有關於樣品之間可能發生的信息。畢竟,在量表值被報告給量度後端時,在量表上設置的任何中間值都會丟失,因此在設置這些中間值時彷佛沒有什麼價值。
若是有幫助的話,能夠把它Gauge看做是一個「海森計(heisen-gauge)」 - 一種只有在觀察時纔會改變的meter 。其餘每種其餘meter 類型均可以累積中間計數,以便將數據發送到metrics 後端。
該MeterRegistry界面包含創建測量儀以觀察數值(values),函數(functions),collections和maps的方法。
List<String> list = registry.gauge("listGauge", Collections.emptyList(), new ArrayList<>(), List::size); (1)
List<String> list2 = registry.gaugeCollectionSize(new ArrayList<>(), "listGauge2"); (2)
Map<String, Integer> map = registry.gaugeMapSize(new HashMap<>(), "mapGauge");
一種稍微更常見的量表是監視一些非數字對象的量表。最後一個參數創建了用於肯定量表觀察時量表值的函數。
(1)的一種更方便的形式,由於當你只是想監視收藏大小時。
建立量表的全部不一樣形式只保留對被觀察對象的弱引用,以避免阻止對象的垃圾收集。
量規可製成以跟蹤任何java.lang.Number亞型即設定,如AtomicInteger和AtomicLong中發現的java.util.concurrent.atomic和相似的類型,如番石榴的AtomicDouble。
AtomicInteger n = registry.gauge("numberGauge", new AtomicInteger(0));
n.set(1);
n.set(2);
請注意,在這種形式下,與其餘meter 類型不一樣,在建立一個Gauge 時,沒有獲得對Gauge 的引用,而是observed。。這是由於海森計劃( heisen-gauge principal)的緣由; gauge 一旦建立就是自給自足的,因此你永遠不須要與它互動。這使咱們可以只給你裝備的對象,它容許快速建立一個能夠建立要觀察的對象併爲其設置metrics 標準的內襯。
這種模式應該比DoubleFunction表格少一些。請記住,常常將觀察Number結果設置爲大量永遠不會發布的中間值。只有發佈時間的量表瞬時值纔會發送到監控系統。
警告 |
試圖用一個原始數字或其一個java.lang對象形式構造一個標尺老是不正確的。這些數字是不可改變的,所以量表不能改變。試圖用一個不一樣的號碼「從新註冊」儀表將不起做用,由於註冊管理機構對於每一個惟一的名稱和標籤組合只維持一米。 |
該界面包含一個流暢的gauges生成器:
Gauge gauge = Gauge
.builder("gauge", myObj, myObj::gaugeValue)
.description("a description of what this gauge does") // optional
.tags("region", "test") // optional
.register(registry);
一般狀況下,返回的Gauge實例除了在測試中沒有用處,由於已經設置了gauge在註冊時自動跟蹤值。
您有責任持續強調您正在測量的狀態對象Gauge。Micrometer 注意不要對可能被垃圾收集的物體產生有力的參考。一旦測量對象被取消參考並進行垃圾收集,根據註冊表的實施狀況,Micrometer將開始報告NaN或沒有任何數據。
若是您看到您的儀表報告幾分鐘,而後消失或報告NaN,那麼幾乎能夠確定地代表所測量的潛在對象已被垃圾收集。
定時器可用於測量短期延遲和此類事件的頻率。Timer報告的全部實現至少將事件的總時間和次數做爲單獨的時間序列。
做爲一個例子,考慮一個圖表,顯示一個典型的Web服務器的請求延遲。預計服務器能夠快速響應許多請求,所以計時器將每秒更新屢次。
出於充分的理由,定時器的適當基本單位因指標後端而異。Micrometer 對此無疑是毫無心義的,但因爲混淆的可能性,TimeUnit在與Timers 相互做用時須要一個。Micrometer 知道每一個實施的偏好,並根據實施將您的時間存儲在適當的基本單元中。
public interface Timer extends Meter {
...
void record(long amount, TimeUnit unit);
void record(Duration duration);
double totalTime(TimeUnit unit);
}
界面包含定時器的流暢構建器:
Timer timer = Timer
.builder("my.timer")
.description("a description of what this timer does") // optional
.tags("region", "test") // optional
.register(registry);
該Timer接口暴露了一些方便過載的內嵌錄製時間,例如:
timer.record(() -> dontCareAboutReturnValue());
timer.recordCallable(() -> returnValue());
Runnable r = timer.wrap(() -> dontCareAboutReturnValue()); (1)
Callable c = timer.wrap(() -> returnValue());
包裝Runnable或Callable返回儀器化版本以備後用。
注意 |
A Timer實際上只是一個專門的分發摘要,它知道如何將持續時間縮放到每一個監控系統的基本時間單位,而且具備自動肯定的基本單位。在任何你想測量時間的狀況下,你應該使用一個Timer而不是一個DistributionSummary。 |
您也能夠將開始狀態存儲在稍後能夠中止的示例實例中。該示例記錄了基於註冊表時鐘的開始時間。啓動樣品後,執行要定時的代碼並經過調用stop(Timer)樣品完成操做。
Timer.Sample sample = Timer.start(registry);// do stuff
Response response = ...
sample.stop(registry.timer("my.timer", "response", response.status()));
請注意咱們如何肯定咱們正在累積樣本的計時器,直到中止採樣爲止。這使咱們可以從咱們計時的操做的最終狀態動態地肯定某些標籤。
這些micrometer-core模塊包含一個@Timed註釋,框架可使用該註釋來爲任何特定類型的方法(例如爲Web請求端點提供服務的方法)或一般爲全部方法添加時序支持。
警告 |
Micrometer的Spring Boot配置沒法識別@Timed任意方法。 |
還包括一個孵化的AspectJ方面micrometer-core,您能夠經過編譯/加載時AspectJ編織或經過以其餘方式(例如Spring AOP)解釋AspectJ方面和代理目標方法的框架工具在您的應用程序中使用。如下是一個Spring AOP配置示例:
@Configuration
public class TimedConfiguration {
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}
應用TimedAspect使@Timed任何方法均可用。
Micrometer 還提供了一種更少使用的定時器模式,可跟蹤兩個單調增長的功能(一個功能保持不變或隨時間增長,但從不減小):計數功能和總時間功能。像普羅米修斯(Prometheus)這樣的一些監控系統會將計數器的累計值(在這種狀況下適用於計數和總時間函數)推送到後端,但其餘監控系統會公佈計數器在推送時間間隔內遞增的速率。經過採用這種模式,您可讓您的監測系統使用Micrometer 實現選擇是否對計時器進行標準化,而且您的計時器能夠在不一樣類型的監測系統中保持可移植性。
IMap<?, ?> cache = ...; // suppose we have a Hazelcast cache
registry.more().timer("cache.gets.latency", Tags.of("name", cache.getName()), cache,
c -> c.getLocalMapStats().getGetOperationCount(), (1)
c -> c.getLocalMapStats().getTotalGetLatency(),
TimeUnit.NANOSECONDS (2)
);
getOperationCount() 是一個單調增長的函數,每一個緩存從其生命的開始就會增長。
這表明的是由時間單位表示的時間單位getTotalGetLatency()。每一個註冊表實現都指定了其預期的基本時間單位,報告的總時間將縮放到此值。
函數跟蹤計時器與監控系統的速率規範化功能(不管這是查詢語言的工件仍是數據推送到系統的方式)一致,在功能的累積值之上增長了一層豐富性他們本身。您能夠推斷吞吐量和延遲的速率,不管該速率是否在可接受範圍內,隨着時間的推移而增長或減小等。
警告 |
Micrometer 不能保證計數和總時間函數的單調性。經過使用這個簽名,你基於你對他們的定義的瞭解而斷言他們的單調性。 |
在FunctionTimer接口自己還有一個功能定時器的流利構建器,能夠訪問基本單元和描述等較少使用的選項。您能夠經過調用將計時器註冊爲其構建的最後一步register(MeterRegistry)。
IMap<?, ?> cache = ...
FunctionTimer.builder("cache.gets.latency", cache,
c -> c.getLocalMapStats().getGetOperationCount(),
c -> c.getLocalMapStats().getTotalGetLatency(),
TimeUnit.NANOSECONDS)
.tags("name", cache.getName())
.description("Cache gets")
.register(registry);
Micrometer使用LatencyUtils軟件包來補償協調的省略 - 系統和VM暫停致使的額外延遲,使您的延遲統計數據向下傾斜。像百分比和SLA計數這樣的分佈統計受暫停檢測器實現的影響,該暫停檢測器實如今這裏和那裏增長額外的等待時間以補償暫停。
Micrometer 支持兩種暫停檢測器實現:基於時鐘漂移的檢測器和NOOP檢測器。默認狀況下,千分表配置時鐘漂移檢測器,以報告儘量準確的指標,無需進一步配置。
基於時鐘漂移的檢測器具備可配置的睡眠間隔和暫停閾值。CPU消耗與成本成反比sleepInterval,暫停檢測精度也是如此。默認狀況下,這兩個值都會設置爲100ms,以提供長時間暫停事件的體面檢測,同時消耗可忽略不計的CPU時間。
您可使用如下自定義暫停檢測器:
registry.config().pauseDetector(new ClockDriftPauseDetector(sleepInterval, pauseThreshold));
registry.config().pauseDetector(new NoPauseDetector());
未來,咱們可能會提供更多的檢測器實現。在某些狀況下,可能會從GC日誌記錄中推斷出一些暫停,例如,不須要恆定的CPU負載,可是最小。將來的JDK也可能提供對暫停事件的直接訪問。
定時器是消耗內存最多的meter,它們的總佔用空間會根據您選擇的選項而發生顯着變化。如下是基於使用各類功能的內存消耗表。這些數字假設沒有標籤和環形緩衝區長度爲3.添加標籤固然會增長總量,增長緩衝區長度也是如此。根據註冊表的實施狀況,總存儲量也可能有所不一樣。
R =環形緩衝區長度。咱們假設在全部示例中缺省爲3。R被設置Timer.Builder#distributionStatisticBufferLength。
B =總直方圖桶。能夠是SLA邊界或百分位直方圖桶。默認狀況下,定時器被限制爲1ms的最小指望值和30秒的最大指望值,在適用的狀況下,產生66個百分桶直方圖桶。
I =暫停補償的區間估計器。1.7 kb
M =最大時間衰減 104字節
Fb =固定的邊界直方圖。30b * B * R
Pp =百分比精度。缺省值爲1.一般在[0,3]範圍內。Pp設置爲Timer.Builder#percentilePrecision。
Hdr(Pp)=高動態範圍直方圖。
當Pp = 0時:1.9kb * R + 0.8kb
當Pp = 1時:3.8kb * R + 1.1kb
當Pp = 2時:18.2kb * R + 4.7kb
當Pp = 3時:66kb * R + 33kb
暫停檢測 |
客戶端百分點 |
直方圖和/或SLA |
式 |
例 |
是 |
沒有 |
沒有 |
I + M |
〜1.8kb的 |
是 |
沒有 |
是 |
I + M + Fb |
對於默認百分直方圖,約7.7kb |
是 |
是 |
是 |
I + M + Hdr(Pp) |
除此以外,還有一個0.95的百分位,默認值爲〜14.3kb |
沒有 |
沒有 |
沒有 |
中號 |
〜0.1kb |
沒有 |
沒有 |
是 |
M + Fb |
對於默認的百分直方圖,大約6kb |
沒有 |
是 |
是 |
M + Hdr(Pp) |
除此以外,還有一個0.95的百分位,默認值爲〜12.6kb |
注意 |
這些估計是基於在Micrometer 1.0.3中所作的改進,並假定至少該版本。 |
注意 |
對於Prometheus來講,R 老是等於1,而無論你如何配置它Timer.Builder。這是Prometheus的特例,由於它預計累積直方圖數據永遠不會翻轉。 |
分發摘要用於跟蹤事件的分佈。它在結構上相似於計時器,但記錄的值不表明時間單位。例如,可使用分發摘要來度量到達服務器的請求的有效負載大小。
要建立分發摘要:
DistributionSummary summary = registry.summary("response.size");
該界面包含流暢的構建器,用於發佈摘要:
DistributionSummary summary = DistributionSummary
.builder("response.size")
.description("a description of what this summary does") // optional
.baseUnit("bytes") // optional (1)
.tags("region", "test") // optional
.scale(100) // optional (2)
.register(registry);
添加基座以實現最大的便攜性 - 基座是某些監控系統的命名約定的一部分。若是你忘記了它,違反命名規則將不會產生不利影響。
或者,您能夠提供一個縮放因子,每一個記錄的樣本在記錄時將被倍增。
Micrometer的預選百分直方圖桶都是從1到最大長度的整數。目前minimumExpectedValue並maximumExpectedValue用來控制鬥集的基數。若是咱們嘗試檢測您的最小/最大值產生一個小範圍,並將預選存儲區域縮放到摘要範圍,那麼咱們沒有另外一個槓桿來控制存儲桶基數。
相反,若是摘要的域名受到更多限制,請按固定因子縮放摘要的範圍。咱們迄今爲止所聽到的用例是其域名爲[0,1]的比率總結。而後:
DistributionSummary.builder("my.ratio").scale(100).register(registry)
經過這種方式,比例在[0,100]範圍內,咱們能夠將其設置maximumExpectedValue爲100.若是您關心特定比率,請將其與自定義SLA邊界配對:
DistributionSummary.builder("my.ratio")
.scale(100)
.sla(70, 80, 90)
.register(registry)
根據您選擇的選項,分發摘要的總內存佔用量可能會有很大差別。如下是基於使用各類功能的內存消耗表。這些數字假設沒有標籤和環形緩衝區長度爲3.添加標籤固然會增長總量,增長緩衝區長度也是如此。根據註冊表的實施狀況,總存儲量也可能有所不一樣。
R =環形緩衝區長度。咱們假設在全部示例中缺省爲3。R被設置DistributionSummary.Builder#distributionStatisticBufferLength。
B =總直方圖桶。能夠是SLA邊界或百分位直方圖桶。默認狀況下,彙總沒有最小和最大指望值,所以將全部276個預約直方圖存儲區發貨。您應該始終使用a minimumExpectedValue和maximumExpectedValue當您打算髮送百分比直方圖時限制發行摘要。
M =最大時間衰減 104字節
Fb =固定的邊界直方圖。30b * B * R
Pp =百分比精度。缺省值爲1.一般在[0,3]範圍內。Pp設置爲DistributionSummary.Builder#percentilePrecision。
Hdr(Pp)=高動態範圍直方圖。
當Pp = 0時:1.9kb * R + 0.8kb
當Pp = 1時:3.8kb * R + 1.1kb
當Pp = 2時:18.2kb * R + 4.7kb
當Pp = 3時:66kb * R + 33kb
客戶端百分點 |
直方圖和/或SLA |
式 |
例 |
沒有 |
沒有 |
中號 |
〜0.1kb |
沒有 |
是 |
M + Fb |
對於夾持到66個桶的百分位直方圖,〜6kb |
是 |
是 |
M + Hdr(Pp) |
除此以外,還有一個0.95的百分位,默認值爲〜12.6kb |
注意 |
這些估計是基於在Micrometer 1.0.3中所作的改進,並假定至少該版本。 |
注意 |
對於Prometheus來講,R 老是等於1,而無論你如何配置它DistributionSummary.Builder。這是Prometheus的特例,由於它預計累積直方圖數據永遠不會翻轉。 |
長時間任務計時器是一種特殊類型的計時器,可以讓您在測量事件仍在運行的同時測量時間。計時器不會記錄任務完成前的持續時間。
如今考慮一個後臺進程來刷新數據存儲中的元數據。例如,Edda能夠緩存AWS資源,例如實例,卷,自動擴展組等。一般全部數據均可以在幾分鐘內刷新。若是AWS服務有問題,則可能須要更長的時間。長時間定時器可用於跟蹤刷新元數據的總時間。
例如,在Spring應用程序中,一般須要使用這種長時間運行的進程@Scheduled。測微儀提供了一個特殊的@Timed註釋,用於經過長時間的任務計時器來測量這些過程。
@Timed(value = "aws.scrape", longTask = true)@Scheduled(fixedDelay = 360000)void scrapeResources() {
// find instances, volumes, auto-scaling groups, etc...
}
這取決於應用程序框架以使事情發生@Timed。若是您選擇的框架不支持它,您仍然可使用長任務計時器:
LongTaskTimer scrapeTimer = registry.more().longTaskTimer("scrape");void scrapeResources() {
scrapeTimer.record(() => {
// find instances, volumes, auto-scaling groups, etc...
});
}
若是咱們想要在此過程超過閾值時發出警報,而且有一個長時間的任務計時器,咱們將在超過閾值後的第一個報告間隔收到該警報。使用普通定時器,咱們不會收到警報,直到過程完成後的第一個報告間隔超過一個小時!
該界面包含用於長任務計時器的流暢構建器:
LongTaskTimer longTaskTimer = LongTaskTimer
.builder("long.task.timer")
.description("a description of what this timer does") // optional
.tags("region", "test") // optional
.register(registry);
定時器和發佈摘要支持收集數據以觀察其百分比分佈。有兩種查看百分位數的主要方法:
百分位直方圖(Percentile histograms) - Micrometer 將值累加到基礎直方圖並將預約的一組水桶運送到監控系統。監控系統的查詢語言負責計算此直方圖的百分數。目前,只有普羅米修斯(Prometheus )和Atlas支持基於直方圖的百分位近似值,經過histogram_quantile和:percentile分別。若是以Prometheus或Atlas爲目標,則更喜歡這種方法,由於您能夠彙總跨維度的直方圖(經過簡單彙總一組維度中的存儲區的值)並從直方圖中導出可彙總的百分位數。
客戶端百分點 (Client-side percentiles)- Micrometer 計算每一個儀表ID(名稱和標籤集)的百分比近似值,並將百分位數值發送給監控系統。這不像百分比直方圖那麼靈活,由於不可能彙總標籤間的百分比近似值。儘管如此,它爲監視系統的百分位分佈提供了一些洞察,這些監視系統不支持基於直方圖的服務器端百分比計算。
如下是用直方圖構建計時器的示例:
Timer.builder("my.timer")
.publishPercentiles(0.5, 0.95) // median and 95th percentile
.publishPercentileHistogram()
.sla(Duration.ofMillis(100))
.minimumExpectedValue(Duration.ofMillis(1))
.maximumExpectedValue(Duration.ofSeconds(10))
publishPercentiles - 這用於發佈您的應用中計算的百分比值。這些值在整個維度上不可聚合。
publishPercentileHistogram- 這用於發佈適用於計算Prometheus中使用histogram_quantile和Atlas使用的聚合(跨維)百分比近似值的直方圖:percentile。生成的直方圖中的存儲桶由Micrometer根據Netflix經驗肯定的發生器進行預設,以產生大多數真實世界定時器和發佈摘要的合理偏差界限。發生器默認產生276個桶,可是千分尺僅運送在minimumExpectedValue和由maximumExpectedValue(包括)設置的範圍內的那些桶。默認狀況下,測微器將定時器固定在1毫秒到1分鐘的範圍內,每一個定時器尺寸產生73個直方圖桶。publishPercentileHistogram對不支持可聚合百分比近似值的系統沒有影響 - 這些系統不提供直方圖。
sla - 用您的SLA定義的桶發佈累積直方圖。與publishPercentileHistogram支持可聚合百分位數的監視系統一塊兒使用時,此設置會在發佈的直方圖中添加更多的存儲桶。在不支持可聚合百分位數的系統上使用時,此設置會致使僅使用這些存儲桶發佈直方圖。
minimumExpectedValue/ maximumExpectedValue- 控制發貨桶的數量,publishPercentileHistogram並控制底層HdrHistogram結構的準確性和內存佔用。
因爲向監控系統傳送百分比會生成額外的時間序列,所以一般最好不要將它們配置爲做爲應用程序依賴關係包含的核心庫中。相反,應用程序能夠經過儀表過濾器爲某些定時器/發佈摘要集打開此行爲。
例如,假設咱們在一個公共庫中有一些定時器。咱們已經爲這些計時器名稱加上了前綴myservice:
registry.timer("myservice.http.requests").record(..);
registry.timer("myservice.db.requests").record(..);
咱們能夠經過儀表過濾器爲兩個定時器啓用客戶端百分比:
registry.config().meterFilter(
new MeterFilter() {
@Override
public HistogramConfig configure(Meter.Id id, HistogramConfig config) {
if(id.getName().startsWith("myservice")) {
return HistogramConfig.builder()
.percentiles(0.95)
.build()
.merge(config);
}
return config;
}
});