SpringBoot2中的度量框架
背景
当一个 Spring Boot 项目运行时,我们可能需要对这个项目进行实时监控,获取项目运行情况,在项目出错时能够自动报警等。
Spring Boot提供了actuator,来帮助开发者获取应用程序的实时运行数据。开发者可以选择使用 HTTP 端点或者 JMX 来管理和监控应用程序,获取应用程序的运行数据,包括健康状况、应用信息、内存使用等情况。
初体验
pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
默认端点介绍
见[2]
健康检查
http://localhost:8080/actuator/health
结果
{
status: "UP"
}
默认只开启了health接口。可以访问 /actuator
来查看开放的接口。
metric
开启web显示,方便调试。
management:
endpoints:
web:
exposure:
include: metrics
endpoint:
metrics:
enabled: true
访问 http://localhost:8080/actuator/metrics
,可见默认的指标有:
{
"names": [
"http.server.requests",
"jvm.buffer.count",
"jvm.buffer.memory.used",
"jvm.buffer.total.capacity",
"jvm.classes.loaded",
"jvm.classes.unloaded",
"jvm.gc.live.data.size",
"jvm.gc.max.data.size",
"jvm.gc.memory.allocated",
"jvm.gc.memory.promoted",
"jvm.gc.pause",
"jvm.memory.committed",
"jvm.memory.max",
"jvm.memory.used",
"jvm.threads.daemon",
"jvm.threads.live",
"jvm.threads.peak",
"jvm.threads.states",
"logback.events",
"process.cpu.usage",
"process.files.max",
"process.files.open",
"process.start.time",
"process.uptime",
"system.cpu.count",
"system.cpu.usage",
"system.load.average.1m",
"tomcat.sessions.active.current",
"tomcat.sessions.active.max",
"tomcat.sessions.alive.max",
"tomcat.sessions.created",
"tomcat.sessions.expired",
"tomcat.sessions.rejected"
]
}
自定义统计指标
除了使用 metrics 端点默认的这些统计指标外,我们还可以实现自定义统计指标。Metrics 提供 4 种基本的度量类型:Gauge、Counter、Timer、Summary。下面分别进行介绍。
Gauge(计量器)
Gauge(计量器)是最简单的度量类型,只有一个简单的返回值,他用来记录一些对象或者事物的瞬时值。
Metrics.gauge("user.test.gauge", 3);
如果要统计Map类型的话,可以使用
Metrics.gaugeMapSize("global.concurrency", Tags.of("ip", NetworkUtil.SERVER_IP), stateMap);
Counter(计数器)
Counter(计数器)简单理解就是一种只增不减的计数器。它通常用于记录服务的请求数量、完成的任务数量、错误的发生数量等等。
static final Counter userCounter = Metrics.counter("user.counter.total", "services", "demo");
public void processCollectResult() {
userCounter.increment(1D);
}
Timer(计时器)
Timer(计时器)可以同时测量一个特定的代码逻辑块的调用(执行)速度和它的时间分布。简单来说,就是在调用结束的时间点记录整个调用块执行的总时间,适用于测量短时间执行的事件的耗时分布,例如消息队列消息的消费速率。
private Timer timer = Metrics.timer("user.test.timer","timer", "timersample");
@GetMapping("/hello")
public void hello() {
// 执行createOrder方法并记录执行时间
timer.record(() -> createOrder());
}
//模拟方法耗时
private void createOrder() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
}
}
默认的timer结果中,只有sum, max和count指标。可以使用Config的方式,增加位点指标。[3]
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class MicrometerConfig {
@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> {
registry.config().meterFilter(
new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
// 显示P50, P90, P95, P99信息
if (id.getType() == Meter.Type.TIMER && id.getName().contains("timer")) {
return DistributionStatisticConfig.builder()
.percentilesHistogram(true)
.percentiles(0.5, 0.90, 0.95, 0.99)
// .sla(Duration.ofMillis(50).toNanos(),
// Duration.ofMillis(100).toNanos(),
// Duration.ofMillis(200).toNanos(),
// Duration.ofSeconds(1).toNanos(),
// Duration.ofSeconds(5).toNanos())
// .minimumExpectedValue(Duration.ofMillis(1).toNanos())
// .maximumExpectedValue(Duration.ofSeconds(5).toNanos())
.build()
.merge(config);
} else {
return config;
}
}
});
};
}
}k
Summary(摘要)
Summary(摘要)用于跟踪事件的分布。它类似于一个计时器,但更一般的情况是,它的大小并不一定是一段时间的测量值。在 micrometer 中,对应的类是 DistributionSummary,它的用法有点像 Timer,但是记录的值是需要直接指定,而不是通过测量一个任务的执行时间。
private DistributionSummary summary = Metrics.summary("user.test.summary","summary", "summarysample");
@GetMapping("/hello")
public void hello() {
summary.record(2D);
summary.record(3D);
summary.record(4D);
}
通过web访问promethous
引入依赖:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.8.4</version>
</dependency>
修改配置文件:
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
metrics:
enabled: true
prometheus:
enabled: true
查看:http://localhost:8080/actuator/prometheus
其它
Prometheus语法
详见prometheus内置函数,或者Prometheus中文文档
increase
平均增长率:获取区间向量中的第一个后最后一个样本并返回其增长量
increase(node_cpu[2m]) / 120
或者用rate函数可以直接计算区间向量v在时间窗口内平均增长速率
rate(node_cpu[2m])
irate
瞬间增长率:通过区间向量中最后两个样本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度
irate(node_cpu[2m])