项目运行指标:micrometer自定义metrics
micrometer自定义metrics
micrometer提供了基于Java的monitor facade,其与springboot应用和prometheus的集成方式如下图展示

上图中展示的很清楚,应用通过micrometer采集和暴露监控端点给prometheus,prometheus通过pull模式来采集监控时序数据信息。之后作为数据源提供给grafana进行展示。
micrometer支持的度量方式及在springboot中的应用示例
Counter
Counter(计数器)简单理解就是一种只增不减的计数器。它通常用于记录服务的请求数量、完成的任务数量、错误的发生数量等等。
-
package com.dxz.producter.monitor;
-
-
import org.springframework.stereotype.Service;
-
-
import io.micrometer.core.instrument.Counter;
-
import io.micrometer.core.instrument.Metrics;
-
-
-
public class CollectorService {
-
-
static final Counter userCounter = Metrics.counter("user.counter.total", "services", "demo");
-
-
public void processCollectResult() throws InterruptedException {
-
-
while (true) {
-
userCounter.increment(1D);
-
}
-
}
-
}
Gauge
Gauge(仪表)是一个表示单个数值的度量,它可以表示任意地上下移动的数值测量。Gauge通常用于变动的测量值,如当前的内存使用情况,同时也可以测量上下移动的"计数",比如队列中的消息数量。
-
package com.dxz.producter.monitor;
-
-
import java.util.ArrayList;
-
import java.util.List;
-
import java.util.concurrent.atomic.AtomicInteger;
-
-
import org.springframework.stereotype.Component;
-
-
import io.micrometer.core.instrument.Gauge;
-
import io.micrometer.core.instrument.ImmutableTag;
-
import io.micrometer.core.instrument.Metrics;
-
import io.micrometer.core.instrument.Tag;
-
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
-
-
-
public class PassCaseMetric {
-
-
List<Tag> init() {
-
ArrayList<Tag> list = new ArrayList() {
-
};
-
list.add(new ImmutableTag("service", "demo"));
-
return list;
-
}
-
-
AtomicInteger atomicInteger = new AtomicInteger(0);
-
-
Gauge passCaseGuage = Gauge.builder("pass.cases.guage", atomicInteger, AtomicInteger::get).tag("service", "demo")
-
.description("pass cases guage of demo").register(new SimpleMeterRegistry());
-
-
AtomicInteger passCases = Metrics.gauge("pass.cases.guage.value", init(), atomicInteger);
-
-
public void handleMetrics() {
-
-
while (true) {
-
if (System.currentTimeMillis() % 2 == 0) {
-
passCases.addAndGet(100);
-
System.out.println("ADD + " + passCaseGuage.measure() + " : " + passCases);
-
} else {
-
int val = passCases.addAndGet(-100);
-
if (val < 0) {
-
passCases.set(1);
-
}
-
System.out.println("DECR - " + passCaseGuage.measure() + " : " + passCases);
-
}
-
}
-
-
}
-
-
}
增加一个controller,触发他们:
-
package com.dxz.producter.web;
-
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.web.bind.annotation.RequestMapping;
-
import org.springframework.web.bind.annotation.RequestMethod;
-
import org.springframework.web.bind.annotation.RestController;
-
-
import com.dxz.producter.monitor.CollectorService;
-
import com.dxz.producter.monitor.PassCaseMetric;
-
-
-
-
public class MonitorController {
-
-
-
CollectorService collectorService;
-
-
-
PassCaseMetric passCaseMetric;
-
-
-
public String counter() throws InterruptedException {
-
collectorService.processCollectResult();
-
return "+1";
-
}
-
-
-
public String gauge() throws InterruptedException {
-
passCaseMetric.handleMetrics();
-
return "+gauge";
-
}
-
-
}
启动springboot应用,可以在http://host:port/actuator/prometheus 看到端点收集到的数据。其他的也是类似的不再一一截图展示。

这里使用了一个true的循环用来展示不断更新的效果。
同样的可以在grafana中看到监控展示信息
Timer
Timer(计时器)同时测量一个特定的代码逻辑块的调用(执行)速度和它的时间分布。简单来说,就是在调用结束的时间点记录整个调用块执行的总时间,适用于测量短时间执行的事件的耗时分布,例如消息队列消息的消费速率。
-
@Test
-
public void testTimerSample(){
-
Timer timer = Timer.builder("timer")
-
.tag("timer", "timersample")
-
.description("timer sample test.")
-
.register(new SimpleMeterRegistry());
-
-
for(int i=0; i<2; i++) {
-
timer.record(() -> {
-
try {
-
TimeUnit.SECONDS.sleep(2);
-
}catch (InterruptedException e){
-
-
}
-
-
});
-
}
-
-
System.out.println(timer.count());
-
System.out.println(timer.measure());
-
System.out.println(timer.totalTime(TimeUnit.SECONDS));
-
System.out.println(timer.mean(TimeUnit.SECONDS));
-
System.out.println(timer.max(TimeUnit.SECONDS));
-
}
响应数据
2
[Measurement{statistic='COUNT', value=2.0}, Measurement{statistic='TOTAL_TIME', value=4.005095763}, Measurement{statistic='MAX', value=2.004500494}]
4.005095763
2.0025478815
2.004500494
Summary
Summary(摘要)用于跟踪事件的分布。它类似于一个计时器,但更一般的情况是,它的大小并不一定是一段时间的测量值。在micrometer中,对应的类是DistributionSummary,它的用法有点像Timer,但是记录的值是需要直接指定,而不是通过测量一个任务的执行时间。
-
@Test
-
public void testSummary(){
-
-
DistributionSummary summary = DistributionSummary.builder("summary")
-
.tag("summary", "summarySample")
-
.description("summary sample test")
-
.register(new SimpleMeterRegistry());
-
-
summary.record(2D);
-
summary.record(3D);
-
summary.record(4D);
-
-
System.out.println(summary.count());
-
System.out.println(summary.measure());
-
System.out.println(summary.max());
-
System.out.println(summary.mean());
-
System.out.println(summary.totalAmount());
-
}
响应数据:
3
[Measurement{statistic='COUNT', value=3.0}, Measurement{statistic='TOTAL', value=9.0}, Measurement{statistic='MAX', value=4.0}]
4.0
3.0
9.0
序
本文主要研究下如何使用自定义micrometer的metrics
实例
DemoMetrics
-
public class DemoMetrics implements MeterBinder {
-
AtomicInteger count = new AtomicInteger(0);
-
-
-
public void bindTo(MeterRegistry meterRegistry) {
-
Gauge.builder("demo.count", count, c -> c.incrementAndGet())
-
.tags("host", "localhost")
-
.description("demo of custom meter binder")
-
.register(meterRegistry);
-
}
-
}
这里实现了MeterBinder接口的bindTo方法,将要采集的指标注册到MeterRegistry
注册
- 原始方式
new DemoMetrics().bindTo(registry);
- springboot autoconfigure
-
-
public DemoMetrics demoMetrics(){
-
return new DemoMetrics();
-
}
在springboot只要标注下bean,注入到spring容器后,springboot会自动注册到registry。springboot已经帮你初始化了包括UptimeMetrics等一系列metrics。详见源码解析部分。
验证
curl -i http://localhost:8080/actuator/metrics/demo.count
返回实例
-
{
-
"name": "demo.count",
-
"measurements": [
-
{
-
"statistic": "VALUE",
-
"value": 6
-
}
-
],
-
"availableTags": [
-
{
-
"tag": "host",
-
"values": [
-
"localhost"
-
]
-
}
-
]
-
}
源码解析
MetricsAutoConfiguration
spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.java
-
-
(Timed.class)
-
(MetricsProperties.class)
-
(CompositeMeterRegistryAutoConfiguration.class)
-
public class MetricsAutoConfiguration {
-
-
-
-
public Clock micrometerClock() {
-
return Clock.SYSTEM;
-
}
-
-
-
public static MeterRegistryPostProcessor meterRegistryPostProcessor(
-
ApplicationContext context) {
-
return new MeterRegistryPostProcessor(context);
-
}
-
-
-
(0)
-
public PropertiesMeterFilter propertiesMeterFilter(MetricsProperties properties) {
-
return new PropertiesMeterFilter(properties);
-
}
-
-
-
(value = "management.metrics.binders.jvm.enabled", matchIfMissing = true)
-
static class JvmMeterBindersConfiguration {
-
-
-
-
public JvmGcMetrics jvmGcMetrics() {
-
return new JvmGcMetrics();
-
}
-
-
-
-
public JvmMemoryMetrics jvmMemoryMetrics() {
-
return new JvmMemoryMetrics();
-
}
-
-
-
-
public JvmThreadMetrics jvmThreadMetrics() {
-
return new JvmThreadMetrics();
-
}
-
-
-
-
public ClassLoaderMetrics classLoaderMetrics() {
-
return new ClassLoaderMetrics();
-
}
-
-
}
-
-
-
static class MeterBindersConfiguration {
-
-
-
(name = { "ch.qos.logback.classic.LoggerContext",
-
"org.slf4j.LoggerFactory" })
-
(LogbackLoggingCondition.class)
-
(LogbackMetrics.class)
-
(value = "management.metrics.binders.logback.enabled", matchIfMissing = true)
-
public LogbackMetrics logbackMetrics() {
-
return new LogbackMetrics();
-
}
-
-
-
(value = "management.metrics.binders.uptime.enabled", matchIfMissing = true)
-
-
public UptimeMetrics uptimeMetrics() {
-
return new UptimeMetrics();
-
}
-
-
-
(value = "management.metrics.binders.processor.enabled", matchIfMissing = true)
-
-
public ProcessorMetrics processorMetrics() {
-
return new ProcessorMetrics();
-
}
-
-
-
(name = "management.metrics.binders.files.enabled", matchIfMissing = true)
-
-
public FileDescriptorMetrics fileDescriptorMetrics() {
-
return new FileDescriptorMetrics();
-
}
-
-
}
-
-
static class LogbackLoggingCondition extends SpringBootCondition {
-
-
-
public ConditionOutcome getMatchOutcome(ConditionContext context,
-
AnnotatedTypeMetadata metadata) {
-
ILoggerFactory loggerFactory = LoggerFactory.getILoggerFactory();
-
ConditionMessage.Builder message = ConditionMessage
-
.forCondition("LogbackLoggingCondition");
-
if (loggerFactory instanceof LoggerContext) {
-
return ConditionOutcome.match(
-
message.because("ILoggerFactory is a Logback LoggerContext"));
-
}
-
return ConditionOutcome
-
.noMatch(message.because("ILoggerFactory is an instance of "
-
+ loggerFactory.getClass().getCanonicalName()));
-
}
-
-
}
-
-
}
可以看到这里注册了好多metrics,比如UptimeMetrics,JvmGcMetrics,ProcessorMetrics,FileDescriptorMetrics等这里重点看使用@Bean标注了MeterRegistryPostProcessor
MeterRegistryPostProcessor
spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.java
-
class MeterRegistryPostProcessor implements BeanPostProcessor {
-
-
private final ApplicationContext context;
-
-
private volatile MeterRegistryConfigurer configurer;
-
-
MeterRegistryPostProcessor(ApplicationContext context) {
-
this.context = context;
-
}
-
-
-
public Object postProcessAfterInitialization(Object bean, String beanName)
-
throws BeansException {
-
if (bean instanceof MeterRegistry) {
-
getConfigurer().configure((MeterRegistry) bean);
-
}
-
return bean;
-
}
-
-
-
private MeterRegistryConfigurer getConfigurer() {
-
if (this.configurer == null) {
-
this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class),
-
beansOfType(MeterFilter.class),
-
(Collection<MeterRegistryCustomizer<?>>) (Object) beansOfType(
-
MeterRegistryCustomizer.class),
-
this.context.getBean(MetricsProperties.class).isUseGlobalRegistry());
-
}
-
return this.configurer;
-
}
-
-
private <T> Collection<T> beansOfType(Class<T> type) {
-
return this.context.getBeansOfType(type).values();
-
}
-
-
}
可以看到这里new了一个MeterRegistryConfigurer,重点注意这里使用beansOfType(MeterBinder.class)方法的返回值给其构造器
MeterRegistryConfigurer
spring-boot-actuator-autoconfigure-2.0.0.RELEASE-sources.jar!/org/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryConfigurer.java
-
class MeterRegistryConfigurer {
-
-
private final Collection<MeterRegistryCustomizer<?>> customizers;
-
-
private final Collection<MeterFilter> filters;
-
-
private final Collection<MeterBinder> binders;
-
-
private final boolean addToGlobalRegistry;
-
-
MeterRegistryConfigurer(Collection<MeterBinder> binders,
-
Collection<MeterFilter> filters,
-
Collection<MeterRegistryCustomizer<?>> customizers,
-
boolean addToGlobalRegistry) {
-
this.binders = (binders != null ? binders : Collections.emptyList());
-
this.filters = (filters != null ? filters : Collections.emptyList());
-
this.customizers = (customizers != null ? customizers : Collections.emptyList());
-
this.addToGlobalRegistry = addToGlobalRegistry;
-
}
-
-
void configure(MeterRegistry registry) {
-
if (registry instanceof CompositeMeterRegistry) {
-
return;
-
}
-
// Customizers must be applied before binders, as they may add custom
-
// tags or alter timer or summary configuration.
-
customize(registry);
-
addFilters(registry);
-
addBinders(registry);
-
if (this.addToGlobalRegistry && registry != Metrics.globalRegistry) {
-
Metrics.addRegistry(registry);
-
}
-
}
-
-
@SuppressWarnings("unchecked")
-
private void customize(MeterRegistry registry) {
-
LambdaSafe.callbacks(MeterRegistryCustomizer.class, this.customizers, registry)
-
.withLogger(MeterRegistryConfigurer.class)
-
.invoke((customizer) -> customizer.customize(registry));
-
}
-
-
private void addFilters(MeterRegistry registry) {
-
this.filters.forEach(registry.config()::meterFilter);
-
}
-
-
private void addBinders(MeterRegistry registry) {
-
this.binders.forEach((binder) -> binder.bindTo(registry));
-
}
-
-
}
可以看到configure方法里头调用了addBinders,也就是把托管给spring容器的MeterBinder实例bindTo到meterRegistry
小结
springboot2引入的micrometer,自定义metrics只需要实现MeterBinder接口,然后托管给spring即可,springboot的autoconfigure帮你自动注册到meterRegistry。
项目运行指标:micrometer自定义metrics_meterregistrycustomizer-CSDN博客
SpringBoot结合prometheus自定义埋点实践_springboot prometheus 自定义-CSDN博客

浙公网安备 33010602011771号