知識庫 / DevOps RSS 訂閱

Micrometer 快速指南

DevOps,Spring
HongKong
4
02:14 PM · Dec 06 ,2025

1. 簡介

Micrometer 提供了一個簡單的面向對象接口,用於對多種流行的監控系統中的指標客户端進行封裝。目前,它支持以下監控系統:Atlas、Datadog、Graphite、Ganglia、InfluxDB、JMX 和 Prometheus。

在本教程中,我們將介紹 Micrometer 的基本用法及其與 Spring 的集成。

為了簡化演示,我們將以 Micrometer Atlas 為例,展示大部分用例。

2. Maven 依賴

為了開始,讓我們將以下依賴項添加到 <em pom.xml</em> 中:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-atlas</artifactId>
    <version>1.16.0</version>
</dependency>

最新版本可以在這裏找到:這裏

3. MeterRegistry

在 Micrometer 中,MeterRegistry 是用於註冊計器的核心組件。我們可以迭代註冊表,並進一步獲取每個計器的指標,以在後端生成時間序列,通過將指標和維度值組合起來。

最簡單的註冊表形式是 SimpleMeterRegistry。但是,在大多數情況下,我們應該明確使用為我們的監控系統設計的 MeterRegistry,例如,對於 Atlas,它是 AtlasMeterRegistry

CompositeMeterRegistry 允許添加多個註冊表。它提供了一種同時將應用程序指標發佈到各種受支持的監控系統解決方案。

我們可以添加任何需要的 MeterRegistry,以便將數據上傳到多個平台:

CompositeMeterRegistry compositeRegistry = new CompositeMeterRegistry();
SimpleMeterRegistry oneSimpleMeter = new SimpleMeterRegistry();
AtlasMeterRegistry atlasMeterRegistry 
  = new AtlasMeterRegistry(atlasConfig, Clock.SYSTEM);

compositeRegistry.add(oneSimpleMeter);
compositeRegistry.add(atlasMeterRegistry);

Micrometer 支持靜態全局註冊表,通過 <em >Metrics.globalRegistry</em></em >。 此外,還提供了基於該全局註冊表的靜態構建器,用於在Metrics 中生成儀表盤:

@Test
public void givenGlobalRegistry_whenIncrementAnywhere_thenCounted() {
    class CountedObject {
        private CountedObject() {
            Metrics.counter("objects.instance").increment(1.0);
        }
    }
    Metrics.addRegistry(new SimpleMeterRegistry());

    Metrics.counter("objects.instance").increment();
    new CountedObject();

    Optional<Counter> counterOptional = Optional.ofNullable(Metrics.globalRegistry
      .find("objects.instance").counter());
    assertTrue(counterOptional.isPresent());
    assertTrue(counterOptional.get().count() == 2.0);
}

4. <em多標籤 和 <em計量器

4.1. 標籤

一個 儀表 的標識符由名稱和標籤組成。我們應該遵循一種將單詞用點分隔的命名約定,以確保指標名稱在多個監控系統中具有可移植性。

Counter counter = registry.counter("page.visitors", "age", "20s");

標籤 可用於對指標進行切片,從而進行價值的推理。 在上面的代碼中,page.visitors 是指標的名稱,age=20s 是其標籤。 在這種情況下,計數器正在統計在 20 到 30 歲年齡段的訪問者數量。

對於大型系統,我們可以將常見的標籤附加到註冊表中。 例如,如果指標來自特定區域:

registry.config().commonTags("region", "ua-east");

4.2. 計數器 (Counter)

一個 Counter 僅報告在應用程序的某個特定屬性上的計數。可以使用流暢構建器或任何 MetricRegistry 的輔助方法來構建自定義計數器:

Counter counter = Counter
  .builder("instance")
  .description("indicates instance count of the object")
  .tags("dev", "performance")
  .register(registry);

counter.increment(2.0);
 
assertTrue(counter.count() == 2);
 
counter.increment(-1);
 
assertTrue(counter.count() == 1);

正如上例所示,我們嘗試將計數器減小一個,但我們只能以固定的正向增量方式增加計數器。

4.3. 定時器 (Timers)

為了測量我們的系統中延遲或事件頻率,我們可以使用 Timer 。一個 Timer 將至少報告特定時間序列的總時間以及事件計數。

例如,我們可以記錄一個可能持續數秒的應用程序事件:

SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = registry.timer("app.event");
timer.record(() -> {
    try {
        TimeUnit.MILLISECONDS.sleep(15);
    } catch (InterruptedException ignored) {
    }
    });

timer.record(30, TimeUnit.MILLISECONDS);

assertTrue(2 == timer.count());
assertThat(timer.totalTime(TimeUnit.MILLISECONDS)).isBetween(40.0, 55.0);

為了記錄長時間運行的事件,我們使用 <a href="https://github.com/micrometer-metrics/micrometer/blob/master/micrometer-core/src/main/java/io/micrometer/core/instrument/LongTaskTimer.java#L26"><em>LongTaskTimer</em></a>:

SimpleMeterRegistry registry = new SimpleMeterRegistry();
LongTaskTimer longTaskTimer = LongTaskTimer
  .builder("3rdPartyService")
  .register(registry);

LongTaskTimer.Sample currentTaskId = longTaskTimer.start();
try {
    TimeUnit.MILLISECONDS.sleep(2);
} catch (InterruptedException ignored) { }
long timeElapsed = currentTaskId.stop();
 
 assertEquals(2L, timeElapsed/((int) 1e6),1L);

4.4. 儀表盤(Gauge)

儀表盤(Gauge)顯示當前讀數。

與其它儀表不同,儀表盤(Gauges) 僅在被觀察時才報告數據。 儀表盤(Gauges) 在監控緩存或集合的統計信息時非常有用:

SimpleMeterRegistry registry = new SimpleMeterRegistry();
List<String> list = new ArrayList<>(4);

Gauge gauge = Gauge
  .builder("cache.size", list, List::size)
  .register(registry);

assertTrue(gauge.value() == 0.0);
 
list.add("1");
 
assertTrue(gauge.value() == 1.0);

4.5. 分佈統計摘要 (DistributionSummary)

事件的分佈以及簡單的摘要信息由 DistributionSummary 提供。

SimpleMeterRegistry registry = new SimpleMeterRegistry();
DistributionSummary distributionSummary = DistributionSummary
  .builder("request.size")
  .baseUnit("bytes")
  .register(registry);

distributionSummary.record(3);
distributionSummary.record(4);
distributionSummary.record(5);

assertTrue(3 == distributionSummary.count());
assertTrue(12 == distributionSummary.totalAmount());

此外,DistributionSummaryTimers 還可以通過百分位數進行增強:

SimpleMeterRegistry registry = new SimpleMeterRegistry();
Timer timer = Timer
  .builder("test.timer")
  .publishPercentiles(0.3, 0.5, 0.95)
  .publishPercentileHistogram()
  .register(registry);

現在,在上面的片段中,將有三個儀表,其標籤分別為 percentile=0.3percentile=0.5,percentile=0.95,在註冊表中可用,指示 95%、50% 和 30% 的觀測值低於以下值,分別對應。

為了觀察這些百分位數在實際應用中的效果,我們添加一些記錄:

timer.record(2, TimeUnit.SECONDS);
timer.record(2, TimeUnit.SECONDS);
timer.record(3, TimeUnit.SECONDS);
timer.record(4, TimeUnit.SECONDS);
timer.record(8, TimeUnit.SECONDS);
timer.record(13, TimeUnit.SECONDS);

然後我們可以通過從這三個分位點 Gauges 中提取值來驗證:

Map<Double, Double> actualMicrometer = new TreeMap<>();
ValueAtPercentile[] percentiles = timer.takeSnapshot().percentileValues();
for (ValueAtPercentile percentile : percentiles) {
    actualMicrometer.put(percentile.percentile(), percentile.value(TimeUnit.MILLISECONDS));
}

Map<Double, Double> expectedMicrometer = new TreeMap<>();
expectedMicrometer.put(0.3, 1946.157056);
expectedMicrometer.put(0.5, 3019.89888);
expectedMicrometer.put(0.95, 13354.663936);

assertEquals(expectedMicrometer, actualMicrometer);

此外,Micrometer 還支持 服務級別目標(直方圖):

DistributionSummary hist = DistributionSummary
  .builder("summary")
  .serviceLevelObjectives(1, 10, 5)
  .register(registry);

類似於分位數,在追加了若干條記錄後,我們可以看到直方圖能夠很好地處理計算:

Map<Integer, Double> actualMicrometer = new TreeMap<>();
HistogramSnapshot snapshot = hist.takeSnapshot();
Arrays.stream(snapshot.histogramCounts()).forEach(p -> {
  actualMicrometer.put((Integer.valueOf((int) p.bucket())), p.count());
});

Map<Integer, Double> expectedMicrometer = new TreeMap<>();
expectedMicrometer.put(1,0D);
expectedMicrometer.put(10,2D);
expectedMicrometer.put(5,1D);

assertEquals(expectedMicrometer, actualMicrometer);

通常,直方圖可以幫助直觀地比較不同分組的數據。此外,直方圖還可以進行時間縮放,這對於分析後端服務響應時間非常有用:

Duration[] durations = {Duration.ofMillis(25), Duration.ofMillis(300), Duration.ofMillis(600)};
Timer timer = Timer
  .builder("timer")
  .sla(durations)
  .publishPercentileHistogram()
  .register(registry);

5. Binders

Micrometer 具有多個內置的 binder,用於監控 JVM、緩存、ExecutorService 和日誌服務。

在 JVM 和系統監控方面,我們可以監控類加載器指標 (ClassLoaderMetrics), JVM 內存池 (JvmMemoryMetrics) 和 GC 指標 (JvmGcMetrics),以及線程和 CPU 利用率 (JvmThreadMetricsProcessorMetrics)。

緩存監控(目前僅支持 Guava、EhCache、Hazelcast 和 Caffeine)通過使用 GuavaCacheMetricsEhCache2MetricsHazelcastCacheMetricsCaffeineCacheMetrics 進行實現。

要監控 Logback 服務,可以將 LogbackMetrics 綁定到任何有效的註冊表:

new LogbackMetrics().bind(registry);

上述引線的使用與 LogbackMetrics 極為相似,都相當簡單,因此此處不再贅述細節。

6. Spring Integration

Spring Boot Actuator 提供 Micrometer 的依賴管理和自動配置。 它已支持 Spring Boot 2.0/1.x 和 Spring Framework 5.0/4.x。

我們需要以下依賴項(最新版本可以在 這裏找到):

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-spring-legacy</artifactId>
    <version>1.3.20</version>
</dependency>

在不修改現有代碼的情況下,我們已啓用 Spring 支持,我們的 Spring 應用程序的 Micrometer JVM 內存指標將自動註冊到全局註冊表中併發布到默認的 atlas 端點:http://localhost:7101/api/v1/publish

有多種可配置的屬性可用於控制指標導出行為,從 spring.metrics.atlas.* 開始。請參閲 AtlasConfig 以查看 Atlas 發佈的所有配置屬性。

如果需要綁定更多的指標,只需將它們作為 @Bean 添加到應用程序上下文。

例如,如果我們需要使用 JvmThreadMetrics

@Bean
JvmThreadMetrics threadMetrics(){
    return new JvmThreadMetrics();
}

關於Web監控,它針對應用程序中的每個端點進行了自動配置,但可以通過配置屬性 spring.metrics.web.autoTimeServerRequests 進行管理。

默認實現為端點提供了四維度的指標:HTTP請求方法、HTTP響應碼、端點URI和異常信息。

當請求被響應時,與請求方法(GETPOST等)相關的指標將發佈到Atlas。

通過 Atlas Graph API,我們可以生成圖表來比較不同方法的響應時間:

默認情況下,20x30x40x50x響應碼也會被報告:

我們還可以比較不同的URI:

或者檢查異常指標:

請注意,我們還可以使用 @Timed 在控制器類或特定端點方法上,以自定義標籤、長時間任務、分位數和百分位數等指標:

@RestController
@Timed("people")
public class PeopleController {

    @GetMapping("/people")
    @Timed(value = "people.all", longTask = true)
    public List<String> listPeople() {
        //...
    }

}

根據以上代碼,我們可以通過檢查 Atlas 端點 http://localhost:7101/api/v1/tags/name 獲得以下標籤:

["people", "people.all", "jvmBufferCount", ... ]

Micrometer 也適用於 Spring Boot 2.0 中引入的函數式 Web 框架。可以通過過濾 RouterFunction 來啓用指標。

RouterFunctionMetrics metrics = new RouterFunctionMetrics(registry);
RouterFunctions.route(...)
  .filter(metrics.timer("server.requests"));

我們還可以從數據源和計劃任務收集指標。有關更多詳細信息,請參閲 官方文檔

7. 結論

本文介紹了 Micrometer 計量服務面。通過抽象並支持多種監控系統,該工具使得在不同的監控平台上輕鬆切換變得非常容易。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.