首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

架构解析系列-OTeL & Micrometer 在 Spring Boot 中的应用与分析

之前在 聊聊 SpringBoot3 的 Micrometer Tracing 这篇文章中我介绍了 SpringBoot3 使用 Micrometer Tracing 来作为分布式链路组件的来龙去脉,在那篇文章中也提及了 SpringBoot  在 可观测性部分官方默认使用的是 Micrometer 来实现。

实际上,在可观测性部分,OpenTelemetry  正在往大一统的方向不断前进,SpringBoot 即时默认使用 Micrometer 来补充其在可观测性上的板块,但是社区也从未停止过对于 SpringBoot 集成 OpenTelemetry 讨论;SpringBoot 实际上先是在 tracing 上完成了对于 OTEL tracing 的桥接,但是对于 metrics 却迟了一些动作,可以从这里看到关于 SpringBoot 对于可观测性的计划 Observability Planning。关于 OpenTelemetry support in Spring 的讨论,在这个 issue 中有比较详细的记录 Consolidate OpenTelemetry support in Spring。

在本篇文章中,我将向您介绍如何使用 OpenTelemetry Java agent 来捕获 SpringBoot Metrics。并希望通过本篇来了解 Micrometer metrics 以及 OpenTelemetry metrics 在指标收集中的差异以及两种报文协议的区别。

环境及前置步骤准备

环境

基本依赖

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

基于 Micrometer metrics 的指标收集

基于前面的环境和前置步骤,搭建一个简单的 SpringBoot 样例工程。前面提到,SpringBoot 默认使用 micrometer 来实现指标的收集,下面在依赖中添加基于 Prometheus 协议的 registry 来暴露 micrometer 指标。

<groupId>io.micrometer</groupId>

<artifactId>micrometer-registry-prometheus</artifactId>

在 配置文件中配置暴露 prometheus endpoint。

启动应用之后,可以通过 http://localhost:8080/actuator/prometheus 来查看默认的一些指标信息。关于指标信息的解释可以查阅 springboot 官方文档 Metrics。这里我添加一个默认的自定义指标 hello.total 来统计调用 /hello 接口的次数,以便于后面来区分不同协议中的表现,代码如下:

请求此接口之后得到的指标信息如下:

  ~ curl -i http://localhost:8080/actuator/prometheus | grep hello_total

# HELP hello_total

# TYPE hello_total counter

hello_total 1.0

到这里,初步完成相关的准备工作,这里提供出去的 /actuator/prometheus 实际上就是我们常规生产环境中对外暴露的监控数据获取的 endpoint,以便于 promethus 来拉取指标数据,这里不再赘述。

使用 OpenTelemetry Collector 来抓取  /actuator/prometheus 数据

首先,需要部署一个  OpenTelemetry Collector ,这里可以根据官方文档来部署,https://opentelemetry.io/docs/collector/installation/。我这里是从 GitHub 下载的可执行文件安装的,可以根据你的操作系统来选择具体的可执行文件下载。然后根据官方文档的配置来创建一个 config.yaml 文件,用于指定相应的 receiversexporters,下面是参考官方文档创建的配置文件:

receivers:

prometheus:

config:

scrape_configs:

- job_name: "springboot-otel-guides"

scrape_interval: 5s # 采集的时间间隔

metrics_path: '/actuator/prometheus' #采集的目标服务对外提供的 metric endpoint path

static_configs:

- targets: ["localhost:8080"] #采集的目标服务地址

exporters:

prometheus:

endpoint: "localhost:8889" #收集器对外暴露访问的地址,这里仅配置了 prometheus 的 exporters

service:

pipelines:

metrics:

receivers: [prometheus]  # 使用 prometheus 协议,对应上面的 receivers#prometheus

processors: [batch]

exporters: [prometheus] # 对应上面 exporters#prometheus

启动 OTEL Collector

./otelcol --config=config.yaml

启动日志大致如下:

Downloads ./otelcol-contrib_0.96.0_darwin_arm64/otelcol-contrib --config=config.yaml

2024-03-21T15:58:26.162+0800    info    service@v0.96.0/telemetry.go:55 Setting up own telemetry...

2024-03-21T15:58:26.162+0800    info    service@v0.96.0/telemetry.go:97 Serving metrics {"address": ":8888", "level": "Basic"}

2024-03-21T15:58:26.162+0800    info    service@v0.96.0/service.go:143  Starting otelcol-contrib... {"Version": "0.96.0", "NumCPU": 8}

2024-03-21T15:58:26.162+0800    info    extensions/extensions.go:34 Starting extensions...

2024-03-21T15:58:26.163+0800    info    prometheusreceiver@v0.96.0/metrics_receiver.go:240  Starting discovery manager  {"kind": "receiver", "name": "prometheus", "data_type": "metrics"}

2024-03-21T15:58:26.164+0800    info    prometheusreceiver@v0.96.0/metrics_receiver.go:231  Scrape job added    {"kind": "receiver", "name": "prometheus", "data_type": "metrics", "jobName": "springboot-otel-guides"}

2024-03-21T15:58:26.164+0800    info    service@v0.96.0/service.go:169  Everything is ready. Begin running and processing data.

2024-03-21T15:58:26.164+0800    warn    localhostgate/featuregate.go:63 The default endpoints for all servers in components will change to use localhost instead of 0.0.0.0 in a future version. Use the feature gate to preview the new default.   {"feature gate ID": "component.UseLocalHostAsDefaultHost"}

2024-03-21T15:58:26.164+0800    info    prometheusreceiver@v0.96.0/metrics_receiver.go:282  Starting scrape manager {"kind": "receiver", "name": "prometheus", "data_type": "metrics"}

通过 http://localhost:8889/metrics 访问,查看之前自定义的 hello_total

  ~ curl -i http://localhost:8889/metrics | grep hello_total

# HELP hello_total

# TYPE hello_total counter

hello_total{instance="localhost:8080",job="springboot-otel-guides"} 1

这里我仅是将 Micrometer 采集的指标数据(本质上使用的是 PromethusMeterRegistry,而不是 Micrometer 默认的 SimpleMeterRegistry)通过 promethus 协议同步到 OpenTelemetry Collector。下面来将 Micrometer 采集的方式换成 OpenTelemetry Java Agent  方式采集。

使用 OpenTelemetry Java Agent 代替 Micrometer

前面我配置了 OpenTelemetry Collector 来抓取 Micrometer 采集的数据,这小节会使用 OpenTelemetry Java Agent 代替 Micrometer 来收集指标,使用 OpenTelemetry Line Protocol (otlp) 协议来提供指标数据给 OpenTelemetry Collector。

1、首先将前面的 config.yaml  配置文件修改使用 otlp 协议

receivers:

otlp:

protocols:

grpc:

http:

exporters:

prometheus:

endpoint: "localhost:8889" # 还是适用 prometheus 对外暴露数据

service:

pipelines:

metrics:

receivers: [otlp] # 这里使用 otlp 协议

processors: [batch]

exporters: [prometheus]

2、下载  OpenTelemetry Java Agent

从  github 上下载  agent,地址:https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases;默认情况下,代理中的指标是禁用的,这里可以通过设置环境变量来启用 OTEL_METRICS_EXPORTER=otlp。

export OTEL_METRICS_EXPORTER=otlp

java -javaagent:opentelemetry-javaagent.jar  -jar target/springboot-otel-guides-0.0.1-SNAPSHOT.jar

3、移除关于 promethus 的配置和依赖

# application.properties 中移除

management.endpoints.web.exposure.include=prometheus

# pom.xml 中移除

<groupId>io.micrometer</groupId>

<artifactId>micrometer-registry-prometheus</artifactId>

<scope>runtime</scope>

重新启动 OTEL Collector 和 应用程序之后,再次访问 /hello,然后查看指标信息

  ~ curl -i http://localhost:8889/metrics | grep hello_total

# HELP hello_total

# TYPE hello_total counter

hello_total{job="springboot-otel-guides"} 1 #指标的表现形式和之前不同

前面两种,我使用了两种不同的采集方式(PromethusMeterRegistry 和 OpenTelemetryMeterRegistry) 和 两种不同的 receivers 方式将指标信息采集到 OpenTelemetry Collector 中。在这篇文章中提到的指标差异问题,在我的测试过程中其实体现不是很明显,从源码调试中捕获到的信息是,Micrometer 的 Metrics.globalRegistry 中,除了opentelemetry-javaagent 中的OpenTelemetryMeterRegistry 之外,还有 Micrometer 自己的 SimpleMeterRegistry,但是实际情况看起来是 runtime 使用了 OpenTelemetryMeterRegistry 而不是 SimpleMeterRegistry,这也就和文章中提及的可能注册"错误的 MeterRegistry 实例" 的条件没有成立。

实际上,SpringBoot 已经默认集成了 otlp exporters,其基本具备和 promethus 同样的接入方式。下面的案例中,将会去除 agent,直接使用 otlp registry。

SpringBoot 使用 otlp registry

因为在前面的步骤中已经移除了 promethus 相关的依赖和配置,应用服务通过集成 agent 来采集指标数据,在下面的测试中,将会移除 agent,然后引入新的依赖,使得能够使用 otlp 协议来发布指标数据。

引入 micrometer-registry-otlp 和 opentelemetry-exporter-otlp 依赖

<dependency>

<groupId>io.micrometer</groupId>

<artifactId>micrometer-registry-otlp</artifactId>

<version>1.12.3</version>

</dependency>

<dependency>

<groupId>io.opentelemetry</groupId>

<artifactId>opentelemetry-exporter-otlp</artifactId>

<version>1.36.0</version>

</dependency>

management.otlp.metrics.export.url=http://localhost:4318/v1/metrics

修改 OpenTelemetry Collector 配置

receivers:

otlp:

protocols: # otlp 对应的两种协议方式

grpc:

endpoint: localhost:4317

http:

endpoint: localhost:4318

exporters:

debug:

verbosity: detailed

file:

path: /Users/glmapper/Downloads/metrics-otlp.json # 为了方便查看,这里我将指标信息直接 export 到文件中

service:

pipelines:

metrics:

receivers: [otlp] # 使用 otlp

exporters: [file] # 使用 file

PS:OtlpMeterRegistry 中默认使用了 http 协议,并通过 http://localhost:4318/v1/metrics 接口向 OpenTelemetry Collector  POST 指标数据。

重新启动 OpenTelemetry Collector 和应用服务程序,下面是访问 /hello 之后,采集到的自定义指标数据如下:

{

"name": "hello.total",

"sum": {

"dataPoints": [

{

"startTimeUnixNano": "1711090372507000000",

"timeUnixNano": "1711090415605000000",

"asDouble": 1

}

],

"aggregationTemporality": 2,

"isMonotonic": true

}

},

从这里数据结构看,与前面使用 promethus 协议的指标数据结构差异是非常大的

# HELP hello_total

# TYPE hello_total counter

hello_total{job="springboot-otel-guides"} 2

这里我把 两种协议版本的数据样例格式提供一下,以便于需要的同学自行查看

otlp v1.1.0 版本的数据格式,其内部通过 opentelemetry/proto/resource/v1/metrics.proto 来描述

从源码角度分析 prometheus 和 otlp 协议的指标收集

这里主要以 micrometer 来看,围绕 micrometer-registry-otlp 和 micrometer-registry-prometheus 两个依赖实现的 MeterRegistry 指标收集代码来分析。MeterRegistry 作为 Micrometer 提供的指标管理的顶层抽象类,其除了提供一组共不同厂商扩展的抽象方法(如 newCounter等)之外,主要核心能力是内部通过维护一个 CHM 类型的 meterMap 来统一管理 Meter 数据。micrometer-registry-otlp 和 micrometer-registry-prometheus  两个包中最核心的也就是对于 MeterRegistry 抽象类的子类扩展实现。从指标数据的透出方式来看, micrometer-registry-otlp  是通过一个定时任务线程池来主动将指标数据 report 给 OpenTelemeter Collector,而 micrometer-registry-prometheus   则是提供一个对外访问的 endpoint 以供收集器来拉取指标数据。

prometheus#PrometheusMeterRegistry

prometheus 的实现 SpringBoot 对外提供的 endpoint 对应的类是 PrometheusScrapeEndpoint,其数据是从它内部持有的 collectorRegistry 对象获取,CollectorRegistry负责维护当前系统中所有的Collector实例。HTTPServer在接收到HTTP请求之后,会从 CollectorRegistry 中拿到所有的Collector实例,并调用其collect()方法获取所有样本,最后格式化为 Prometheus 的标准输出。CollectorRegistry 是 prometheus 客户端提供的能力。同样持有 CollectorRegistry 还有 PrometheusMeterRegistry,前面提到在 micrometer 中所有的指标数据是存在 meterMap 中的,但是在创建 Meter 时,PrometheusMeterRegistry 也会将 Meter 通过 applyToCollector 方法同步到 CollectorRegistry,这样就实现了 prometheus 和 micrometer 对于指标数据的链接。

otlp#OtlpMeterRegistry

这里主要分析下 OtlpMeterRegistry 的 push 模型,OtlpMeterRegistry 实现了 micrometer PushMeterRegistry 类,其内部提供了 publish 方法的实现,OtlpMeterRegistry#publish 实现了 meter 到 OpenTelemetry 的具体逻辑。

当流量进入之后,经过 /hello 接口中的自定义埋点代码,此时 meterRegistry 的注入实例为 OtlpMeterRegistry;但是实际上 OtlpMeterRegistry 对于 meter 数据并没有像 prometheus 那样还提供了内部自己模型的桥接逻辑 ,OtlpMeterRegistry 数据的管理是完全依托于 MeterRegistry 的。

从架构角度分析 OpenTelemetry Collector 数据流向

这里包括前面实例中的几种场景,首先是 OpenTelemetry Collector pull 应用服务 prometheus endpoint 吐出的数据,otlp collector 使用的是 receiver 和 expose 使用的是 prometheus 协议。

第二个是使用 OpenTelemetry Java instrumentation agent 收集指标的数据流向

最后是使用 micrometer-registry-otlp  方式,它使用的是类似于 micrometer-registry-prometheus  通过业务系统中通过代码打点的方式采集的,但是上报方式是和 agent 上报是一致的,这里就不单独提供数据流程示意图了。

总结

本文主要是研究 SpringBoot 如何集成 metrics ,文章中提供了基于 micrometer 和 agent 两种指标采集的形式;基于 micrometer 的方式又细分了基于 prometheus 协议和 otlp 协议两种方式。并且在使用 Otel collector 作为指标数据的中间组件,并提供了相应的实示例代码和示意图。希望通过本文能够帮助大家使用和理解 Springboot 中对于 micrometer 和 otlp 收集指标数据的基本流程和原理。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OpkINBpNQeRvfF9FSAkfX1yg0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券