關于 metrics 我最早接觸相關概念的就是 prometheus,它是第二個加入 CNCF(云原生)社區的項目(第一個是 kubernetes),可見在云原生領域 Metrics 指標監控從誕生之初就是一個非常重要的組件。
現實也確實如此,如今只要使用到了 kubernetes 相關的項目,對其監控就是必不可少的。
當然也不止是云原生的項目才需要 Metrics 指標監控,我們任何一個業務都是需要的,不然我們的服務運行對開發運維來說都是一個黑盒,無法知道此時系統的運行情況,因此才需要我們的業務系統將一些關鍵運行指標暴露出來。
圖片
業務數據:比如訂單的增長率、銷售金額等業務數據;同時還有應用自身的資源占用情況:
在使用 OpenTelemetry 之前,因為 prometheus 是這部分的絕對標準,所以我們通常都會使用 prometheus 的包來暴露這些指標:
<!-- The client --><dependency> <groupId>io.prometheus</groupId> <artifactId>simpleclient</artifactId> <version>0.16.0</version></dependency><!-- Hotspot JVM metrics--><dependency> <groupId>io.prometheus</groupId> <artifactId>simpleclient_hotspot</artifactId> <version>0.16.0</version></dependency>
暴露一個自定義的指標也很簡單:
import io.prometheus.client.Counter;class YourClass { static final Counter requests = Counter.build() .name("requests_total").help("Total requests.").register(); void processRequest() { requests.inc(); // Your code here. }}
這是暴露一個單調遞增的指標,prometheus 還提供了其他幾種指標類型:
之后我們只需要在 prometheus 中配置一些抓取規則即可:
scrape_configs: - job_name: 'springboot' scrape_interval: 10s static_configs: - targets: ['localhost:8080'] # Spring Boot ip+port
當然如果是運行在 kubernetes 環境,prometheus 也可以基于服務發現配置一些規則,自動抓取我們的 Pod 的數據,由于不是本文的重點就不過多介紹。
在 OpenTelemetry 中自然也提供了 Metrics 這個組件,同時它也是完全兼容 Prometheus,所以我們理解和使用起來并不復雜。
不同于 prometheus 客戶端中直接提供了 Counter 就可以創建指標了,在 OpenTelemetry 中會提供一個 MeterProvider 的接口,使用這個接口可以獲取 Meter,再使用 Meter 才可以創建 Counter、Gauge、Histogram 等數據。
下面來看看具體如何使用,這里我以 Pulsar 源碼的代碼進行演示:
public InstrumentProvider(OpenTelemetry otel) { if (otel == null) { // By default, metrics are disabled, unless the OTel java agent is configured. // This allows to enable metrics without any code change. otel = GlobalOpenTelemetry.get(); } this.meter = otel.getMeterProvider() .meterBuilder("org.apache.pulsar.client") .setInstrumentationVersion(PulsarVersion.getVersion()) .build(); }LongCounterBuilder builder = meter.counterBuilder(name) .setDescription(description) .setUnit(unit.toString());
Meter Exporter 則是一個 OpenTelemetry 獨有的概念,與我們之前講到的一樣:OpenTelemetry 作為廠商無關的平臺,允許我們將數據寫入到任何兼容的產品里。
所以我們在使用 Metrics 時需要指定一個 exporter:
Exporter 類型 | 作用 | 備注 | 參數 |
OTLP Exporter | 通過 OpenTelemetry Protocol(OTLP) 發送指標數據到 collect。 | 默認生產環境中推薦使用,需要將數據發送到支持 OTLP 的后端,如 OpenTelemetry Collector。 | -Dotel.metrics.exporter=otlp (default) |
Console Exporter | 將指標數據打印到控制臺的導出器。 | 開發和調試,快速查看指標數據。 | -Dotel.metrics.exporter=console |
Prometheus Exporter | 將指標數據以 Prometheus 抓取的格式暴露給 Prometheus 服務。 | 與 Prometheus 集成,適用于需要 Prometheus 監控的場景,這個可以無縫和以往使用 prometheus 的場景兼容 | -Dotel.metrics.exporter=prometheus |
與 prometheus 類似,OpenTelemetry 也提供了以下幾種指標類型:
同時每個指標還有以下幾個字段:
messageInCounter = meter .counterBuilder(MESSAGE_IN_COUNTER) .setUnit("{message}") .setDescription("The total number of messages received for this topic.") .buildObserver();
還是以 Pulsar 的為例,messageInCounter 是一個記錄總的消息接收數量的 Counter 類型。
subscriptionCounter = meter .upDownCounterBuilder(SUBSCRIPTION_COUNTER) .setUnit("{subscription}") .setDescription("The number of Pulsar subscriptions of the topic served by this broker.") .buildObserver();
這是記錄一個訂閱者數量的指標,類型是 UpDownCounter,也就是可以增加減少的指標。
private static final List<Double> latencyHistogramBuckets = Lists.newArrayList(.0005, .001, .0025, .005, .01, .025, .05, .1, .25, .5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0);DoubleHistogramBuilder builder = meter.histogramBuilder("pulsar.client.producer.message.send.duration") .setDescription("Publish latency experienced by the application, includes client batching time") .setUnit(Unit.Seconds.toString()) .setExplicitBucketBoundariesAdvice(latencyHistogramBuckets);
這是一個記錄 Pulsar producer 發送延遲的指標,類型是 Histogram。
backlogQuotaAge = meter .gaugeBuilder(BACKLOG_QUOTA_AGE) .ofLongs() .setUnit("s") .setDescription("The age of the oldest unacknowledged message (backlog).") .buildObserver();
這是一個記錄最大 unack 也就是 backlog 時間的指標,類型是 Gauge。
在之前的文章:實戰:如何編寫一個 OpenTelemetry Extensions中講過如何開發一個 OpenTelemetry 的 extension,其實當時我就是開發了一個用于在 Pulsar 客戶端中暴露指標的一個插件。
不過目前 Pulsar 社區已經集成了該功能。
其中的核心代碼與上面講到的類似:
public static void registerObservers() { Meter meter = MetricsRegistration.getMeter(); meter.gaugeBuilder("pulsar_producer_num_msg_send") .setDescription("The number of messages published in the last interval") .ofLongs() .buildWithCallback( r -> recordProducerMetrics(r, ProducerStats::getNumMsgsSent));private static void recordProducerMetrics(ObservableLongMeasurement observableLongMeasurement, Function<ProducerStats, Long> getter) { for (Producer producer : CollectionHelper.PRODUCER_COLLECTION.list()) { ProducerStats stats = producer.getStats(); String topic = producer.getTopic(); if (topic.endsWith(RetryMessageUtil.RETRY_GROUP_TOPIC_SUFFIX)) { continue; } observableLongMeasurement.record(getter.apply(stats), Attributes.of(PRODUCER_NAME, producer.getProducerName(), TOPIC, topic)); }}
只是這里使用了 buildWithCallback 回調函數,OpenTelemetry 會每隔 30s 調用一次這個函數,通常適用于 Gauge 類型的數據。
java -javaagent:opentelemetry-javaagent.jar / -Dotel.javaagent.extensinotallow=ext.jar / -Dotel.metrics.exporter=prometheus / -Dotel.exporter.prometheus.port=18180 / -jar myapp.jar
配合上 Prometheus 的兩個啟動參數就可以在本地 18180 中獲取到指標數據:
curl http://127.0.0.1:18180/metrics
當然也可以直接發往 OpenTelemetry-Collector 中,再由它發往 prometheus,只是這樣需要額外在 collector 中配置一下:
exporters: debug: {} otlphttp: metrics_endpoint: http://promethus:8480/insert/0/opentelemetry/api/v1/pushservice: pipelines: metrics: exporters: - otlphttp processors: - k8sattributes - batch receivers: - otlp
圖片
這樣我們就可以在 Grafana 中通過 prometheus 查詢到數據了。
有一點需要注意,如果我們自定義的指標最好是參考官方的語義和命名規范來定義這些指標名稱。
圖片
比如 OpenTelemetry 的規范中名稱是用 . 來進行分隔的。
切換為 OpenTelemetry 之后自然就不需要依賴 prometheus 的包,取而代之的是 OTel 的包:
compileOnly 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.34.1' compileOnly 'io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:1.32.0'
相對來說 Metrics 的使用比 Trace 簡單的多,同時 Metrics 其實也可以和 Trace 進行關聯,也就是 Exemplars,限于篇幅就不在本文展開了,感興趣的可以自行查閱。
參考鏈接:
本文鏈接:http://www.tebozhan.com/showinfo-26-93865-0.html從 Prometheus 到 OpenTelemetry:指標監控的演進與實踐
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com