54. 度量指標(Metrics)
Spring Boot執行器包含一個支持'gauge'和'counter'級別的度量指標服務,'gauge'記錄一個單一值,'counter'記錄一個增量(增加或減少)。同時,Spring Boot提供一個PublicMetrics接口,你可以實現它,從而暴露以上兩種機制不能記錄的指標,具體參考SystemPublicMetrics。
所有HTTP請求的指標都被自動記錄,所以如果點擊metrics端點,你可能會看到類似以下的響應:
{
"counter.status.200.root": 20,
"counter.status.200.metrics": 3,
"counter.status.200.star-star": 5,
"counter.status.401.root": 4,
"gauge.response.star-star": 6,
"gauge.response.root": 2,
"gauge.response.metrics": 3,
"classes": 5808,
"classes.loaded": 5808,
"classes.unloaded": 0,
"heap": 3728384,
"heap.committed": 986624,
"heap.init": 262144,
"heap.used": 52765,
"mem": 986624,
"mem.free": 933858,
"processors": 8,
"threads": 15,
"threads.daemon": 11,
"threads.peak": 15,
"uptime": 494836,
"instance.uptime": 489782,
"datasource.primary.active": 5,
"datasource.primary.usage": 0.25
}
此處,我們可以看到基本的memory,heap,class loading,processor和thread pool信息,連同一些HTTP指標。在該實例中,root('/'),/metrics URLs分別返回20次,3次HTTP 200響應,同時可以看到root URL返回了4次HTTP 401(unauthorized)響應。雙星號(star-star)來自於被Spring MVC /**匹配到的請求(通常為靜態資源)。
gauge展示了一個請求的最后響應時間,所以root的最后請求響應耗時2毫秒,/metrics耗時3毫秒。
注 在該示例中,我們實際是通過HTTP的/metrics路徑訪問該端點的,這也就是響應中出現metrics的原因。
54.1 系統指標
Spring Boot會暴露以下系統指標:
- 系統內存總量(mem),單位:KB
- 空閑內存數量(mem.free),單位:KB
- 處理器數量(processors)
- 系統正常運行時間(uptime),單位:毫秒
- 應用上下文(應用實例)正常運行時間(instance.uptime),單位:毫秒
- 系統平均負載(systemload.average)
- 堆信息(heap,heap.committed,heap.init,heap.used),單位:KB
- 線程信息(threads,thread.peak,thead.daemon)
- 類加載信息(classes,classes.loaded,classes.unloaded)
- 垃圾收集信息(gc.xxx.count, gc.xxx.time)
54.2 數據源指標
Spring Boot會為應用中定義的每個支持的DataSource暴露以下指標:
- 活動連接數(datasource.xxx.active)
- 連接池當前使用情況(datasource.xxx.usage)
所有數據源指標共用datasoure.前綴,該前綴適用於每個數據源:
- 如果是主數據源(唯一可用的數據源或注解@Primary的數據源)前綴為datasource.primary。
- 如果數據源bean名稱以DataSource結尾,前綴就是bean的名稱去掉DataSource的部分(比如,batchDataSource的前綴是datasource.batch)。
- 其他情況使用bean的名稱作為前綴。
通過注冊自定義版本的DataSourcePublicMetrics bean,你可以覆蓋部分或全部的默認行為。Spring Boot默認提供支持所有數據源的元數據,如果喜歡的數據源恰好不被支持,你可以添加其他的DataSourcePoolMetadataProvider beans,具體參考DataSourcePoolMetadataProvidersConfiguration。
54.3 緩存指標
Spring Boot會為應用中定義的每個支持的緩存暴露以下指標:
- cache當前大小(cache.xxx.size)
- 命中率(cache.xxx.hit.ratio)
- 丟失率(cache.xxx.miss.ratio)
注 緩存提供商沒有以一致的方式暴露命中/丟失率,有些暴露的是聚合(aggregated)值(比如,自從統計清理后的命中率),而其他暴露的是時序(temporal)值 (比如,最后一秒的命中率),具體查看緩存提供商的文檔。
如果兩個不同的緩存管理器恰巧定義了相同的緩存,緩存name將以CacheManager bean的name作為前綴。
注冊自定義版本的CachePublicMetrics可以部分或全部覆蓋這些默認值,Spring Boot默認為EhCache,Hazelcast,Infinispan,JCache和Guava提供統計。如果喜歡的緩存庫沒被支持,你可以添加其他CacheStatisticsProvider beans,具體可參考CacheStatisticsAutoConfiguration。
54.4 Tomcat session指標
如果你使用Tomcat作為內嵌的servlet容器,Spring Boot將自動暴露session指標,httpsessions.active和httpsessions.max分別提供活動的和最大的session數量。
54.5 記錄自己的指標
將CounterService或GaugeService注入到你的bean中可以記錄自己的度量指標:CounterService暴露increment,decrement和reset方法;GaugeService提供一個submit方法。
下面是一個簡單的示例,它記錄了方法調用的次數:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.CounterService;
import org.springframework.stereotype.Service;
@Service
public class MyService {
private final CounterService counterService;
@Autowired
public MyService(CounterService counterService) {
this.counterService = counterService;
}
public void exampleMethod() {
this.counterService.increment("services.system.myservice.invoked");
}
}
注 你可以將任何字符串用作度量指標的名稱,但最好遵循所選存儲/圖形技術的指南,Matt Aimonetti’s Blog中有一些好的關於Graphite的指南。
54.6 添加自己的公共指標
只要注冊其他的PublicMetrics實現beans,你就可以添加其他的度量指標,比如計算metrics端點每次調用的次數。默認情況下,端點會聚合所有這樣的beans,通過定義自己的MetricsEndpoint可以輕易改變這種情況。
54.7 使用Java8的特性
Spring Boot提供的GaugeService和CounterService默認實現依賴於你使用的Java版本。如果使用Java8(或更高版本),Spring Boot將實現切換為一個高性能版本,該版本優化了寫速度,底層使用原子內存buffers,而不是通過不可變但相對昂貴的Metric<?>類型(跟基於倉庫的實現相比,counters大概快5倍,gauges大概快2倍)。對於Java7,Dropwizard指標服務也是很有效的(使用了某些Java8並發庫),但它不記錄指標值的時間戳。如果需要關注指標采集的性能,建議你使用高性能的選項,並不要頻繁讀取指標信息,這樣寫入會本地緩存,只有在需要時讀取。
注 如果使用Java8或Dropwizard,Spring Boot默認不會使用老的MetricRepository和它的InMemoryMetricRepository實現。
54.8 指標寫入,導出和聚合
Spring Boot提供幾個標記接口Exporter的實現,可用於將從內存buffers讀取的指標復制到一個分析和展示它們的地方。實際上,如果提供一個實現MetricWriter接口(或GaugeWriter用於簡單場景)且注解@ExportMetricWriter的@Bean,它將自動掛鈎一個Exporter並每5秒反饋下指標更新(通過spring.metrics.export.delay-millis配置)。此外,你定義的所有注解@ExportMetricReader的MetricReader,它們的值將被默認exporter導出。
默認exporter是一個MetricCopyExporter,它會優化自己不去復制那些從上次調用以來沒有變化的值(設置spring.metrics.export.send-latest標識可以關閉該優化)。注意Dropwizard MetricRegistry不支持時間戳,所以如果你使用Dropwizard指標服務,該優化是不起作用的(每次都會復制全部指標)。
通過spring.metrics.export.*屬性可以設置導出的觸發器(delay-millis,includes,excludes和send-latest),特殊MetricWriters的值可以通過spring.metrics.export.triggers.<name>.*設置,此處<name>是bean的名稱(或匹配bean名稱的表達式)。
注 如果關閉默認的MetricRepository(比如使用Dropwizard指標服務),指標的自動導出將禁用。你可以通過聲明自定義類型的MetricReader並注解@ExportMetricReader來獲取相同功能。
54.8.1 示例: 導出到Redis
如果提供一個RedisMetricRepository類型的@Bean並注解@ExportMetricWriter,指標將導出到Redis緩存完成聚合。RedisMetricRepository有兩個重要參數用於配置實現這樣的目的:prefix和key(傳遞給構造器)。最好使用應用實例唯一的前綴(比如,使用一個隨機值及應用的邏輯name,這樣可以關聯相同應用的其他實例)。“key”用來保持所有指標name的全局索引,所以它應該全局唯一,不管這對於你的應用意味着什么(比如,相同系統的兩個實例可以共享一個Redis緩存,如果它們有不同的keys)。
示例:
@Bean
@ExportMetricWriter
MetricWriter metricWriter(MetricExportProperties export) {
return new RedisMetricRepository(connectionFactory,
export.getRedis().getPrefix(), export.getRedis().getKey());
}
</pre>
application.properties:
<pre>spring.metrics.export.redis.prefix: metrics.mysystem.${spring.application.name:application}.${random.value:0000}
spring.metrics.export.redis.key: keys.metrics.mysystem
前綴最后由應用名和id組成,所以它可以用來標識具有相同邏輯名的processes分組。
注 設置key和prefix都是非常重要的。key用於所有的倉庫操作,並可以被多個倉庫共享。如果多個倉庫共享一個key(比如你需要聚合它們的時候),你通常有一個只讀“master”倉庫,它有一個簡短的但可辨識的前綴(比如metrics.mysystem),還有很多只寫的倉庫,這些倉庫以master前綴開頭(比如以上示例中為metrics.mysystem.*)。這樣從一個"master"倉庫讀取所有keys是相當高效的,但使用較長的前綴讀取一個子集就比較低效了(比如使用一個寫倉庫)。
注 以上示例使用MetricExportProperties去注入和提取key和前綴,這是Spring Boot提供的便利設施,用於配置合適的默認值,你也可以自己設值。
54.8.2 示例: 導出到Open TSDB
如果提供一個OpenTsdbGaugeWriter類型的@Bean並注解@ExportMetricWriter,指標將導出到Open TSDB 完成聚合。OpenTsdbGaugeWriter有一個url屬性,你需要將它設置為Open TSDB的“/put”端點,比如localhost:4242/api/put。它還有個namingStrategy,你可以自定義或配置以使指標匹配服務器上你需要的數據結構。默認它只傳遞指標名作為Open TSDB指標名,添加domain標簽(值為org.springframework.metrics)和process(值為命名策略的對象hash值)。因此,在運行應用並產生一些指標后,你可以在TSD UI查看這些指標(默認路徑為localhost:4242)。
示例:
curl localhost:4242/api/query?start=1h-ago&m=max:counter.status.200.root
[
{
"metric": "counter.status.200.root",
"tags": {
"domain": "org.springframework.metrics",
"process": "b968a76"
},
"aggregateTags": [],
"dps": {
"1430492872": 2,
"1430492875": 6
}
}
]
54.8.3 示例: 導出到Statsd
想要將指標導出到Statsd,首先你需要確定添加了com.timgroup:java-statsd-client依賴(Spring Boot為它提供了依賴管理),然后將spring.metrics.export.statsd.host屬性添加到application.properties文件中,連接將在8125端口建立,除非設置spring.metrics.export.statsd.port對默認值進行覆蓋。使用spring.metrics.export.statsd.prefix可以設置自定義前綴,此外,你可以提供一個StatsdMetricWriter類型的@Bean並注解@ExportMetricWriter:
@Value("${spring.application.name:application}.${random.value:0000}")
private String prefix = "metrics";
@Bean
@ExportMetricWriter
MetricWriter metricWriter() {
return new StatsdMetricWriter(prefix, "localhost", 8125);
}
54.8.4 示例: 導出到JMX
如果提供一個JmxMetricWriter類型並注解@ExportMetricWriter的@Bean,指標將作為MBeans暴露到本地服務器(只要開啟,Spring Boot JMX自動配置會提供MBeanExporter)。
示例:
@Bean
@ExportMetricWriter
MetricWriter metricWriter(MBeanExporter exporter) {
return new JmxMetricWriter(exporter);
}
每個指標都暴露為單獨的MBean,你可以將ObjectNamingStrategy注入JmxMetricWriter來指定ObjectNames的格式。
54.9 聚合多個來源的指標
Spring Boot提供一個AggregateMetricReader,用於合並來自不同物理來源的指標。具有相同邏輯指標的來源只需將指標加上以句號分隔的前綴發布出去,reader會聚合它們(通過截取指標名並丟掉前綴),計數器被求和,所有東西(比如gauges)都采用最近的值。
這非常有用,特別是當有多個應用實例反饋數據到中央倉庫(比如Redis),並且你想展示結果。推薦將MetricReaderPublicMetrics結果連接到/metrics端點。
示例:
@Autowired
private MetricExportProperties export;
@Bean
public PublicMetrics metricsAggregate() {
return new MetricReaderPublicMetrics(aggregatesMetricReader());
}
private MetricReader globalMetricsForAggregation() {
return new RedisMetricRepository(this.connectionFactory,
this.export.getRedis().getAggregatePrefix(), this.export.getRedis().getKey());
}
private MetricReader aggregatesMetricReader() {
AggregateMetricReader repository = new AggregateMetricReader(
globalMetricsForAggregation());
return repository;
}
注 上面的示例使用MetricExportProperties注入和提取key和前綴,這是Spring Boot提供的便利設施,並且默認值是合適的,它們是在MetricExportAutoConfiguration中設置的。
注 上面的MetricReaders不是@Beans,也沒注解@ExportMetricReader,因為它們只收集和分析來自其他倉庫的數據,不需要暴露自己的值。
54.10 Dropwizard指標
當你聲明對io.dropwizard.metrics:metrics-core的依賴時,Spring Boot會創建一個默認的MetricRegistry bean。如果需要自定義,你可以注冊自己的@Bean實例。使用Dropwizard ‘Metrics’ library的用戶會發現Spring Boot指標自動發布到com.codahale.metrics.MetricRegistry,來自MetricRegistry的指標也自動暴露到/metrics端點。
使用Dropwizard指標時,默認的CounterService和GaugeService被DropwizardMetricServices替換,它是一個MetricRegistry的包裝器(所以你可以@Autowired其中任意services,並像平常那么使用它)。通過使用恰當的前綴類型標記你的指標名可以創建特殊的Dropwizard指標服務(比如,gauges使用timer.*,histogram.*,counters使用meter.*)。
54.11 消息渠道集成
如果存在名為metricsChannel的MessageChannel bean,Spring Boot將創建一個MetricWriter將指標寫入該渠道(channel)。writer自動掛鈎一個exporter,所以全部指標值都會出現在渠道上, 訂閱者就可以進行其他分析或動作(提供渠道和訂閱者取決於你)。
