该文档内容来自官方文档,包含部分自己的理解。
[toc]
Micrometer 为大多数流行的监控系统的客户端提供了一个门面(facade)。可以用来监控你的 JVM-based 应用,而不需要为每个监控系统都写一个单独的实现。Micrometer 就像监控界的 SLF4J。
A Meter is the interface for collecting a set of measurements (which we individually call metrics) about your application.
Meter 是采集度量(Metric)的接口。
Micrometer 中的 Meter 包括 Timer
, Counter
, Gauge
, DistributionSummary
, LongTaskTimer
, FunctionCounter
, FunctionTimer
,TimeGauge
.
Meters in Micrometer are created from and held in a MeterRegistry.
在 Micrometer 中,Meter 在 MeterRegistry 中创建,并保存在 MeterRegistry 中。
Each supported monitoring system has an implementation of MeterRegistry.
每个支持的监控系统都有一个 MeterRegistry 的实现
Micrometer provides a CompositeMeterRegistry to which multiple registries can be added, allowing you to publish metrics to more than one monitoring system simultaneously.
Micrometer 提供了 CompositeMeterRegistry 来添加多个 Registry,允许您同时将指标发布到多个监控系统。
如果你的项目基于 Spring Boot 2.0.0.M5 及以上的版本,需引入spring-boot-starter-actuator
。
如果你的项目基于 Spring Boot 1.5.x,则需要引入 micrometer-spring-legacy
:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-spring-legacy</artifactId>
<version>1.1.3</version>
</dependency>
如果你的项目基于 Spring Boot 更低的版本,快升级到 Spring Boot 1.5.x 或者更高吧。
曾经在 2018-09-17 统计各项目依赖版本时, Spring Boot 版本定下的版本号是
1.5.13.RELEASE
。
Registry 是 Micrometer 用来输出监控数据的地方。Micrometer 默认的 Registry (SimpleMeterRegistry) 不会向任何地方输出。所以需要加入额外的 Registry,现在使用的方案是 Prometheus:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.1.3</version>
</dependency>
并且在application.yml
中添加如下配置:
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: health, info, prometheus
至少需要暴露health
, info
, prometheus
这三个 Endpoint.
其他请参照官方文档
如果应用没有办法使用自动配置(比如 Spring Boot 1.x 或者没有使用 spring 的 web 相关的组件), 也可以手动实现一个 web 接口调用io.micrometer.prometheus.PrometheusMeterRegistry#scrape()
暴露监控数据.
比如:
@RestController
@RequestMapping("/")
public class PrometheusMetricController {
@Autowired private PrometheusMeterRegistry meterRegistry;
@GetMapping(value = "/actuator/prometheus", produces = "text/plain; version=0.0.4; charset=utf-8")
public String metric() {
return this.meterRegistry.scrape();
}
}
对于非 Web 应用则需要使用 JMX Registry,由监控平台来"代理"发送数据:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-jmx</artifactId>
<version>1.1.3</version>
</dependency>
- "代理"部分的实现涉及到 pushgateway 及其 java api,有兴趣的同学可以自行研究。
micrometer-registry-prometheus
内已经包含了有关pushgateway
的类,不过请不要直接使用这部分的功能。
同样,其他不同 Registry 只需要引入对应的依赖即可。
在添加监控项之前,可以先了解下已经默认实现的监控项,避免重复造轮子。
- ClassLoader 相关监控
- 堆内存,非堆内存监控
- GC 监控
- 线程池监控
- CPU 占用监控
这部分没有具体研究过,下面只是根据各 AutoConfiguration 类进行的推测
- RestTemplate/AsyncRestTemplate 监控
- Jetty/Tomcat/Servlet 监控
- 数据库连接池监控
- 监控各级别日志的消息个数
- Hibernate EntityManager 监控
- Kafka consumer 监控
简而言之,Counter 只增不减,Gauge 绑定数据,Timer 记录持续时间
这三种基本的 Meter 可以覆盖大部分的情况了
下面将简单介绍三种指标的用法与适用场景。
一般来说,请使用这种方式将默认的 MeterRegistry 注入:
@Autowired
private MeterRegistry registry;
Counter 可以由以下方式进行初始化。 Fluent builder:
Counter counter = Counter
.builder("counter")
.baseUnit("beans") // optional
.description("a description of what this counter does") // optional
.tags("region", "test") // optional
.register(registry);
调用 Counter 中的 increment()方法来计数。
下面这三种使用方式都是可以的。
counter.increment();
counter.increment(5);
counter.increment(1.2);
注: increment()方法的参数不可为负数,否则会带来非预期的错误。
适用场景:
- 方法调用次数,速率等
- 定时任务执行次数
注: 所有有关速率的监控项(请求的 tps,IO 的 ops),只需要添加记录总数的 counter 即可,速率的计算由监控系统实现
Gauge 不像 Counter 那样可以调用increment()
方法手动修改值,gauge 需要绑定一个方法才能进行工作。
Fluent builder:
Gauge gauge = Gauge
.builder("gauge", myObj, myObj::gaugeValue)
.description("a description of what this gauge does") // optional
.tags("region", "test") // optional
.register(registry);
适用场景: 能够直接通过 getter 获取到值的监控,如某个 Collection 的 size。
当然 Gauge 也可以调用 set() 方法手动设置值。
Timer 的使用方式比较多样:
Timer timer = Timer
.builder("my.timer")
.description("a description of what this timer does") // optional
.tags("region", "test") // optional
.register(registry);
timer 初始化后,可以使用多种方式进行记录数据:
- 调用
record()
直接记录一个时间段 - 调用
record()
直接执行并记录 Runnable 对象的执行时间 - 调用
wrap()
包装一个 Runnable 或 Callable 对象
Timer.Sample
用法:
Sample
是Timer
的内部类,对计时这个操作提供了一个更好用的封装,精度是nanoseconds
。Timer.Sample
类的定义很少,只有 20 行。
可以使用 Timer 的start
方法返回一个Timer.Sample
对象,然后使用Timer.Sample
的stop
方法将计时器的数据存入 registry 中。
示例:
Timer.Sample sample = Timer.start(meterRegistry);
/**
* codes here
**/
sample.stop(meterRegistry.timer('meter_name', "exception", "None","tag1", "value1", "tag2", "value2"));
除此之外,还有@Timed
这种用法,可以去官方文档中查看资料。
Timer 只会在任务结束时才统计数据,对于长时间的计算任务,可以用
LongTaskTimer
进行监控,可以监控到任务已经运行了多久。
Spring Boot 2 可以直接通过/actuator/metrics
访问到所有监控项目
Spring Boot 1.5.x 的/metrics
不能访问到 micrometer 建立的监控项
如果引入了micrometer-registry-prometheus
:
Spring Boot 1.5.x 请访问/prometheus
Spring Boot 2 请访问/actuator/prometheus
如果引入了micrometer-registry-jmx
:
请使用jconsole
或其他工具查看micrometer
下的MBean
- 在为监控项命名时,请全部使用英文小写,并使用
.
将各单词隔开。 (dot.case) - 务必清楚的写明
description
字段 - 务必不要在 tags 中使用
job
,instance
作为 key,这两个词是监控系统的保留字,其值在持久化时会被覆盖 - Meter 和 Logger 类似,Logger 以 logger name 为 id,Meter 以 name 和 tags 为 id。当以已经存在的 id 去初始化 Meter 时,会返回已存在的 Meter 实例。
- 对于内容大致相同的监控项,建议使用 tags 而不是 name 进行区分。例如默认实现中的 http request 监控,name 都是
http.server.requests
,增加了三个 tag:method
,url
和status
来区分不同的请求. - 对于同一个 Meter,tag 的数量是一致的,且 value 不能为 null。所以当某个 tag 的值需要为空时,也请不要使用空字符串,而是使用某个约定的值,如字符串
None
(参考WebMvcTags
,line 53,private static final Tag EXCEPTION_NONE = Tag.of("exception", "None");
).
命名示例:
http.server.requests
database.calls
jvm.heap.memory.used
命名反例:
http_server_requests
databaseCalls
health 的实现不属于 micrometer, 但是也是监控中比较重要的部分。
该接口在 Spring Cloud 中是自动发现来判断服务正常与否的关键指标。
简单来说,为应用增加一个 HealthIndicator,只需要实现AbstractHealthIndicator
,然后注入到 Spring 上下文中就可以。
例子:
@Component
public class GrpcHealthIndicator extends AbstractHealthIndicator {
@Autowired
private GrpcServerRunner runner;
@Override
protected void doHealthCheck(Health.Builder builder) {
int count = runner.getGrpcServiceBeans().size();
builder.withDetail("gRPC Services Count", count).withDetail("gRPC Services", runner.getGrpcServiceBeans());
if (count > 0) {
builder.up();
} else {
builder.down();
}
}
}
更多详情请参考官方文档
与 health 一样, info 的实现也不属于 micrometer。
希望大家能够把项目名,版本等信息放到这里来,在应用 Spring Cloud 之前,监控平台将会用 /actuator/info 接口实现组件的自动发现。
spring-boot-maven-plugin
的spring-boot:build-info
可以将组件的信息写入到META-INF/build-info.properties
文件中,需要配置一个 execution:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
<!-- 这个 execution 会写入 build-info.properties-->
<execution>
<id>build-info</id>
<phase>compile</phase>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
也可以将配置写到application.yml
或者自己实现InfoContributor
.
更多详情请参考官方文档.