指标-自定义
依赖
// actuator依赖提供指标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.6.3</version>
</dependency>
// 将指标转换成prometheus可使用的格式
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.8.2</version>
</dependency>
ps:
1、以下指标名称都为prometheus接收的指标名称
2、http://ip:port/actuator/prometheus 此为prometheus默认开放接口,输出prometheus格式
3、http://localhost:11130/actuator/metrics 此为actuator的指标接口,输出当前开放的指标
SpringBoot自定义指标
注入依赖
要注册自定义指标,需要注入MeterRegistry
到你的组件
import io.micrometer.core.instrument.MeterRegistry;
@Autowired
private MeterRegistry meterRegistry;
Meters(指标)
Counter(计数器)
Counter
是一种比较简单的Meter
,它是一种单值的度量类型,或者说是一个单值计数器。Counter接口允许使用者使用一个固定值(必须为正数)进行计数。准确来说:Counter
就是一个增量为正数的单值计数器。这里举个很简单的使用例子:
1、使用meterRegistry
创建指标
Counter counter = meterRegistry.counter("http.request", "createOrder", "/order/create");
counter.increment();
2、使用Counter
内部建造器类Counter.Builder
去实例化Counter
。
Counter counter = Counter.builder("name") //名称
.baseUnit("unit") //基础单位
.description("desc") //描述
.tag("tagKey", "tagValue") //标签
.register(meterRegistry);//绑定的MeterRegistry
counter.increment();
3、FunctionCounter
是Counter
的特化类型,它把计数器数值增加的动作抽象成接口类型ToDoubleFunction
,这个接口JDK1.8中对于Function
的特化类型接口。FunctionCounter
的使用场景和Counter
是一致的。FunctionCounter
使用的一个明显的好处是,我们不需要感知FunctionCounter
实例的存在,实际上我们只需要操作作为FunctionCounter
实例构建元素的AtomicInteger
实例即可,这种接口的设计方式在很多框架里面都可以看到。
AtomicInteger n = new AtomicInteger(0);
//这里ToDoubleFunction匿名实现其实可以使用Lambda表达式简化为AtomicInteger::get
FunctionCounter.builder("functionCounter", n, AtomicInteger::get)
.baseUnit("unit") //基础单位
.description("desc") //描述
.tag("tagKey", "tagValue") //标签
.register(meterRegistry);
n.incrementAndGet(); // 仅对自增api有效
使用场景:
Counter
的作用是记录XXX的总量或者计数值,适用于一些增长类型的统计,例如下单、支付次数、Http请求总量记录等等,通过Tag可以区分不同的场景,对于下单,可以使用不同的Tag标记不同的业务来源或者是按日期划分,对于Http请求总量记录,可以使用Tag区分不同的URL。
gauge(量规)
Gauge
是获取当前度量记录值的句柄,也就是它表示一个可以任意上下浮动的单数值度量Meter。Gauge
通常用于变动的测量值,测量值用ToDoubleFunction
参数的返回值设置,如当前的内存使用情况,同时也可以测量上下移动的”计数”,比如队列中的消息数量。Gauge
的典型使用场景是用于测量集合或映射的大小或运行状态中的线程数。Gauge
一般用于监测有自然上界的事件或者任务,而Counter
一般使用于无自然上界的事件或者任务的监测,所以像Http请求总量计数应该使用Counter
而非Gauge
。MeterRegistry
中提供了一些便于构建用于观察数值、函数、集合和映射的Gauge
相关的方法:
1、使用meterRegistry
创建指标
List<String> list = registry.gauge("listGauge", Collections.emptyList(), new ArrayList<>(), List::size);
List<String> list2 = registry.gaugeCollectionSize("listSize2", Tags.empty(), new ArrayList<>());
Map<String, Integer> map = registry.gaugeMapSize("mapGauge", Tags.empty(), new HashMap<>());
上面的三个方法通过MeterRegistry
构建Gauge
并且返回了集合或者映射实例,使用这些集合或者映射实例就能在其size
变化过程中记录这个变更值。更重要的优点是,我们不需要感知Gauge
接口的存在,只需要像平时一样使用集合或者映射实例就可以了。此外,Gauge
还支持java.lang.Number的子类,java.util.concurrent.atomic包中的AtomicInteger和AtomicLong,还有Guava提供的AtomicDouble:
AtomicInteger n = registry.gauge("numberGauge", new AtomicInteger(0));
n.set(1);
n.set(2);
2、建造器流式创建:
List<Integer> list = new ArrayList<>();
//一般我们不需要操作Gauge实例
Gauge gauge = Gauge.builder("gauge", list, List::size)
.baseUnit("unit") //基础单位
.description("desc") //描述
.tag("tagKey", "tagValue") //标签
.register(meterRegistry);
使用场景:
1、有自然(物理)上界的浮动值的监测,例如物理内存、集合、映射、数值等。
2、有逻辑上界的浮动值的监测,例如积压的消息、(线程池中)积压的任务等,其实本质也是集合或者映射的监测。
timers(计时器)
Time
适用于记录耗时比较短的事件的执行时间,通过时间分布展示事件的序列和发生频率。所有的Timer
的实现至少记录了发生事件的数量和这些事件的总耗时,从而生成一个时间序列。Timer
的基本单位基于服务端的指标而定,但是实际上我们不需要过于关注Timer
的基本单位,因为Micrometer
在存储生成的时间序列的时候会自动选择适当的基本单位。Timer
接口提供的常用方法如下:
1、使用meterRegistry
创建指标
Timer print1000 = meterRegistry.timer("print1000");
print1000.record(()->{
// 传入任务,并自行执行
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
});
Timer print1000 = meterRegistry.timer("print1000");
// 只定义了指标以及任务,不自行执行
Runnable wrap = print1000.wrap(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
});
// 执行后指标才有值
wrap.run();
2、建造器流式创建:
Timer print1000 = Timer.builder("print1000")
.description("desc") //描述
.tag("tagKey", "tagValue") //标签
.register(meterRegistry);
Runnable wrap = print1000.wrap(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
});
wrap.run();
3、Timer
的使用还可以基于它的内部类Timer.Sample
,通过start
和stop
两个方法记录两者之间的逻辑的执行耗时。
Timer.Sample sample = Timer.start(meterRegistry);
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
sample.stop(meterRegistry.timer("print1000"));
4、FunctionTimer
是Timer
的特化类型,它主要提供两个单调递增的函数(其实并不是单调递增,只是在使用中一般需要随着时间最少保持不变或者说不减少),一个用于计数的函数和一个用于记录总调用耗时的函数。
官方文档中的例子如下:
IMap<?, ?> cache = ...; // 假设使用了Hazelcast缓存
registry.more().timer("cache.gets.latency", Tags.of("name", cache.getName()), cache,
c -> c.getLocalMapStats().getGetOperationCount(), //实际上就是cache的一个方法,记录缓存生命周期初始化的增量(个数)
c -> c.getLocalMapStats().getTotalGetLatency(), // Get操作的延迟时间总量,可以理解为耗时
TimeUnit.NANOSECONDS
);
countFunction
用于统计事件个数,totalTimeFunction
用于记录执行总时间,实际上两个函数都只是Function
函数的变体,还有一个比较重要的是总时间的单位totalTimeFunctionUnit
。简单的使用方式如下:
//这个是为了满足参数,暂时不需要理会
Object holder = new Object();
AtomicLong totalTimeNanos = new AtomicLong(0);
AtomicLong totalCount = new AtomicLong(0);
FunctionTimer.builder("functionTimer",
holder,
p -> totalCount.get(), // 统计事件个数
p -> totalTimeNanos.get(), // 记录执行总时间
TimeUnit.NANOSECONDS) // 总时间的单位
.description("desc") //描述
.tag("tagKey", "tagValue") //标签
.register(meterRegistry);
totalTimeNanos.addAndGet(10000000);
totalCount.incrementAndGet();
使用场景:
1、记录指定方法的执行时间用于展示。
2、记录一些任务的执行时间,从而确定某些数据来源的速率,例如消息队列消息的消费速率等。
long task timer(长任务定时器)
LongTaskTimer
也是一种Timer
的特化类型,主要用于记录长时间执行的任务的持续时间,在任务完成之前,被监测的事件或者任务仍然处于运行状态,任务完成的时候,任务执行的总耗时才会被记录下来,而无返回的Timer记录的是任务启动间隔时间
。在Spring应用中,可以简单地使用@Scheduled
和@Timed
注解,基于spring-aop完成定时调度任务的总耗时记录:
1、@Timed注解使用
@Timed(value = "aws.scrape", longTask = true)
@Scheduled(fixedDelay = 360000)
void scrapeResources() {
//这里做相对耗时的业务逻辑
}
2、使用meterRegistry
创建指标
LongTaskTimer print1000 = meterRegistry.more().longTaskTimer("print1000");
print1000.record(()->{
// 传入任务,并自行执行
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
});
3、建造器流式创建:
LongTaskTimer print1000 = LongTaskTimer.builder("print1000")
.description("desc") //描述
.tag("tagKey", "tagValue") //标签
.register(meterRegistry);
print1000.record(()->{
// 传入任务,并自行执行
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
});
使用场景:
LongTaskTimer
适合用于长时间持续运行的事件耗时的记录,例如相对耗时的定时任务。
distribution summary(分布式摘要)
Summary
主要用于跟踪事件的分布,在Micrometer
中,对应的类是Distribution Summary(分发摘要)
。它的使用方式和Timer
十分相似,但是它的记录值并不依赖于时间单位。常见的使用场景:使用Distribution Summary
测量命中服务器的请求的有效负载大小。
1、使用meterRegistry
创建指标
DistributionSummary summer = meterRegistry.summary("summer");
summer.record(0.25);
2、建造器流式创建:
DistributionSummary register = DistributionSummary.builder("summer")
.description("desc") //描述
.tag("tagKey", "tagValue") //标签
.register(meterRegistry);
register.record(0.25);
使用场景
不依赖于时间单位的记录值的测量,例如服务器有效负载值,缓存的命中率等。
常用标签
常用标签可以对所有指标进行添加通用标签
management.metrics.tags.application=${spring.application.name}
management.metrics.tags.tagKey=tagValue
禁用指标
下面以http.server.requests示例禁用
management.metrics.enable.http.server.requests=false