Micrometer 自定义 metrics

Micrometer 自定义 metricsMicrometer 提供了基于 Java 的 monitorfacad 其与 springboot 应用和 prometheus 的集成方式如下图展示上图中展示得很清楚 应用通过 Mic

Micrometer 提供了基于 Java的 monitor facade,其与 springboot 应用和 prometheus 的集成方式如下图展示

0b901e5df2b9658ec362c68bd803c269.png

上图中展示得很清楚,应用通过Micrometer采集和暴露监控端点给prometheus,prometheus通过pull模式来采集监控时序数据信息。之后作为数据源提供给grafana进行展示。

Micrometer支持的度量方式及在springboot中的应用示例

Counter
Counter(计数器)简单理解就是一种只增不减的计数器。它通常用于记录服务的请求数量、完成的任务数量、错误的发生数量等等。

package com.olive.producter.monitor; import org.springframework.stereotype.Service; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.Metrics; @Service("collectorService") 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.olive.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; @Component("passCaseMetric") public class PassCaseMetric { List 
   
     init() { ArrayList 
    
      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.olive.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.olive.producter.monitor.CollectorService; import com.olive.producter.monitor.PassCaseMetric; @RestController @RequestMapping("/monitor") public class MonitorController { @Autowired CollectorService collectorService; @Autowired PassCaseMetric passCaseMetric; @RequestMapping(value = "/counter", method = RequestMethod.GET) public String counter() throws InterruptedException { collectorService.processCollectResult(); return "+1"; } @RequestMapping(value = "/gauge", method = RequestMethod.GET) public String gauge() throws InterruptedException { passCaseMetric.handleMetrics(); return "+gauge"; } }

45c5af63ac08c71d8b1b4c82601a648d.png

这里使用了一个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.00}, Measurement{statistic='MAX', value=2.00}] 4.00 2.00 2.00

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); @Override 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
@Bean 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

@Configuration @ConditionalOnClass(Timed.class) @EnableConfigurationProperties(MetricsProperties.class) @AutoConfigureBefore(CompositeMeterRegistryAutoConfiguration.class) public class MetricsAutoConfiguration { @Bean @ConditionalOnMissingBean public Clock micrometerClock() { return Clock.SYSTEM; } @Bean public static MeterRegistryPostProcessor meterRegistryPostProcessor( ApplicationContext context) { return new MeterRegistryPostProcessor(context); } @Bean @Order(0) public PropertiesMeterFilter propertiesMeterFilter(MetricsProperties properties) { return new PropertiesMeterFilter(properties); } @Configuration @ConditionalOnProperty(value = "management.metrics.binders.jvm.enabled", matchIfMissing = true) static class JvmMeterBindersConfiguration { @Bean @ConditionalOnMissingBean public JvmGcMetrics jvmGcMetrics() { return new JvmGcMetrics(); } @Bean @ConditionalOnMissingBean public JvmMemoryMetrics jvmMemoryMetrics() { return new JvmMemoryMetrics(); } @Bean @ConditionalOnMissingBean public JvmThreadMetrics jvmThreadMetrics() { return new JvmThreadMetrics(); } @Bean @ConditionalOnMissingBean public ClassLoaderMetrics classLoaderMetrics() { return new ClassLoaderMetrics(); } } @Configuration static class MeterBindersConfiguration { @Bean @ConditionalOnClass(name = { "ch.qos.logback.classic.LoggerContext", "org.slf4j.LoggerFactory" }) @Conditional(LogbackLoggingCondition.class) @ConditionalOnMissingBean(LogbackMetrics.class) @ConditionalOnProperty(value = "management.metrics.binders.logback.enabled", matchIfMissing = true) public LogbackMetrics logbackMetrics() { return new LogbackMetrics(); } @Bean @ConditionalOnProperty(value = "management.metrics.binders.uptime.enabled", matchIfMissing = true) @ConditionalOnMissingBean public UptimeMetrics uptimeMetrics() { return new UptimeMetrics(); } @Bean @ConditionalOnProperty(value = "management.metrics.binders.processor.enabled", matchIfMissing = true) @ConditionalOnMissingBean public ProcessorMetrics processorMetrics() { return new ProcessorMetrics(); } @Bean @ConditionalOnProperty(name = "management.metrics.binders.files.enabled", matchIfMissing = true) @ConditionalOnMissingBean public FileDescriptorMetrics fileDescriptorMetrics() { return new FileDescriptorMetrics(); } } static class LogbackLoggingCondition extends SpringBootCondition { @Override 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; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MeterRegistry) { getConfigurer().configure((MeterRegistry) bean); } return bean; } @SuppressWarnings("unchecked") private MeterRegistryConfigurer getConfigurer() { if (this.configurer == null) { this.configurer = new MeterRegistryConfigurer(beansOfType(MeterBinder.class), beansOfType(MeterFilter.class), (Collection 
   
     >) (Object) beansOfType( MeterRegistryCustomizer.class), this.context.getBean(MetricsProperties.class).isUseGlobalRegistry()); } return this.configurer; } private 
    
      Collection 
     
       beansOfType(Class 
      
        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 
   
     > customizers; private final Collection 
    
      filters; private final Collection 
     
       binders; private final boolean addToGlobalRegistry; MeterRegistryConfigurer(Collection 
      
        binders, Collection 
       
         filters, Collection 
        
          > 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。

source: //www.cnblogs.com/duanxz/p/10179946.html

75a842cdbe3c65024a6016fa1f0a7487.png

分享&在看

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/206895.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午3:15
下一篇 2026年3月19日 下午3:15


相关推荐

  • 优先队列的数据结构_低优先级队列一天只能一场

    优先队列的数据结构_低优先级队列一天只能一场目录一.PriorityQueuePriorityQueue简介继承关系PriorityQueue示例二.Comparable比较器Compara接口三.Comparator比较器Comparator接口四.底层原理一.PriorityQueuePriorityQueue简介PriorityQueue,即优先级队列。优先级队列可以保证每次取出来的元素都是队列中的最小或最大的元素<Java优先级队列默认每次取出来的为最小元素&gt.

    2026年2月20日
    4
  • Windows 技术篇-LDSGameMaster文件夹有什么用,删除方法

    Windows 技术篇-LDSGameMaster文件夹有什么用,删除方法LDS是鲁大师的拼写,应该是用过鲁大师,偷偷给你安装的。分析:没什么用,流氓程序,还很大占地方,4个G,可以放心的卸掉。卸载方法:找到里面的卸载程序来卸载,卸载完后把文件夹删除就好了。

    2022年6月14日
    70
  • 图片url地址的生成获取方法

    图片url地址的生成获取方法图片url地址的生成获取方法  在写博客插入图片时,许多时候需要提供图片的url地址。作为菜鸡的我,自然是一脸懵逼。那么什么是所谓的url地址呢?又该如何获取图片的url地址呢?  首先来看一下度娘对url地址的解释:url是统一资源定位符,对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的url,它包含的信息指出文件的位置以及浏览器应该怎么处理它。  简单来说,url地址是是用来定位、访问网上资源用的。常见的网址也属于url地

    2026年2月15日
    4
  • Dubbo原理解析

    Dubbo原理解析Dubbo 原理解析系列文章 http blog csdn net quhongwei zhanqiu article details

    2025年8月30日
    6
  • 计算空间点到平面的投影点坐标(代码)

    计算空间点到平面的投影点坐标(代码)1 pp 为所求的投影点坐标 2 A 为平面上任意已知点 3 n 为平面上的法线 n 的计算方法 一般会已知平面上两个以上的点坐标 例如我是为了求点在任意三角形上的投影点 我当然会知道三角形的三个点坐标 通过其中两个点坐标可以求出法向量 n 假设知道三角形的三个顶点 A x y z B x y z C x y z AB Bx Ax By Ay Bz Az

    2026年3月19日
    2
  • 什么是API?(看完你就理解了)

    什么是API?(看完你就理解了)阅读编程资料时经常会看到 API 这个名词 网上各种高大上的解释估计放倒了一批初学者 初学者看到下面这一段话可能就有点头痛了 API ApplicationP 应用程序编程接口 是一些预先定义的函数 目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力 而又无需访问源码 或理解内部工作机制的细节 百度百科这篇文章我们就来通俗易懂地

    2026年3月17日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号