Java SpringBoot Prometheus

Micrometer简介

SpringBoot整合Prometheus - 图1
Micrometer 为 Java 平台上的性能数据收集提供了一个通用的 API,应用程序只需要使用 Micrometer 的通用 API 来收集性能指标即可。Micrometer 会负责完成与不同监控系统的适配工作。这就使得切换监控系统变得很容易。Micrometer 还支持推送数据到多个不同的监控系统。Micrometer类似日志系统中SLF4J。
Micrometer目前支持的监控系统有:https://micrometer.io/docs
Micrometer中有两个最核心的概念,分别是是计量器(Meter)和计量器注册表(MeterRegistry),下面来分别看下这两个概念。

计量器(Meter)

Meter用来收集性能指标数据(Metris),总共有四种类型的Meter,分别是CounterGaugeTimerSummary
每个Meter都有自己的名称,同时Meter可以指定一系列的tag。tag是以key-value的形式出现,这样就可以根据tag对指标进行过滤。除了每个Meter独有的标签外,也可以通过MeterRegistry添加通用的tag。

  1. MeterRegistry.Config config = simpleMeterRegistry.config();
  2. config.commonTags("tag1","value1","tag2","value2");

Counter

Counter只允许增加值,Counter所表示的计数值是double类型,默认情况下增加的值是1.0

  1. @Autowired
  2. private SimpleMeterRegistry simpleMeterRegistry;
  3. @Bean
  4. public Counter counter1(){
  5. return Counter.builder("test.count1").register(simpleMeterRegistry);
  6. }
  7. @Bean
  8. public Counter counter2(){
  9. return simpleMeterRegistry.counter("test.count2");
  10. }
  11. @Test
  12. public void test(){
  13. counter1.increment();
  14. }

Gauge

Cauge是表示单个的变化的值,例如温度,气压。与Counter的区别在于,Gauge的值不总是增加的

  1. public void guage(){
  2. Gauge.builder("guaua1", this::getValue).register(simpleMeterRegistry);
  3. }
  4. public double getValue(){
  5. return ThreadLocalRandom.current().nextDouble();
  6. }

Gauge对象一旦被创建,就不能手动对其中的值进行修改。在每次取样时,Gauge 会返回当前值

Timer

Timer通常用来记录事件的持续时间。Timer会记录两类的数据,事件的数量和总的持续时间。Timer提供了不同方式来记录持续时间。第一种方式是使用record()方法来记录RunnableCallable对象的运行时间,第二种方式是使用Timer.Sample来保存计时状态

  1. public void record(){
  2. Timer timer = simpleMeterRegistry.timer("record");
  3. timer.record(() -> {
  4. try {
  5. Thread.sleep(3000);
  6. }catch (Exception e){
  7. e.printStackTrace();
  8. }
  9. });
  10. }
  11. public void sample(){
  12. Timer.Sample sample = Timer.start();
  13. new Thread(()->{
  14. try {
  15. Thread.sleep(3000);
  16. }catch (Exception e){
  17. e.printStackTrace();
  18. }
  19. sample.stop(simpleMeterRegistry.timer("sample"));
  20. });
  21. }

summary

summary用来记录指标的分布,summary根据每个指标的值,把值分配到对应的bucket中。Micrometer默认的bucket的值从1到Long.MAX_VALUE,可以通过minimumExpectedValuemaximumExpectedValue来控制bucket的范围,如果指标的值较小,还可以通过scale来设置一个值对数值进行放大

  1. public void summary(){
  2. DistributionSummary summary = DistributionSummary.builder("summary")
  3. .maximumExpectedValue(10L)
  4. .minimumExpectedValue(1L)
  5. .publishPercentiles(0.5, 0.75, 0.9)
  6. .register(simpleMeterRegistry);
  7. summary.record(1.0);
  8. summary.record(5.0);
  9. summary.record(4.5);
  10. summary.record(3.0);
  11. System.out.println(summary.takeSnapshot());
  12. }

计量器注册表(MeterRegistry)

MeterRegistry负责创建和维护Meter。每一个监控系统有自己独有的registry
2021-08-02-23-30-51-356435.png
其中SimpleMeterRegistry是一个基于内存的注册表,它不支持导出数据到监控系统,主要用来进行本地开发和测试。
Micrometer支持多个不同的监控系统,通过CompositeMeterRegistry可以把多个计量器注册表组合起来,从而允许同时发布数据到多个监控系统中。

  1. public void compositeRegistry(){
  2. CompositeMeterRegistry compositeMeterRegistry = new CompositeMeterRegistry();
  3. compositeMeterRegistry.add(new SimpleMeterRegistry());
  4. compositeMeterRegistry.add(new SimpleMeterRegistry(new SimpleConfig() {
  5. @Override
  6. public String get(String s) {
  7. return null;
  8. }
  9. //增加前缀
  10. @Override
  11. public String prefix() {
  12. return "simple";
  13. }
  14. },Clock.SYSTEM));
  15. Counter counter = compositeMeterRegistry.counter("test");
  16. counter.increment();
  17. }

Micrometer本身提供了一个静态的全局注册表Metrics.golbalRegistry。这个注册表一个组合注册表,使用Metrics类中的静态方法创建的计量器,都会被添加到这个全局注册表中

  1. public void globalRegistry(){
  2. Metrics.addRegistry(simpleMeterRegistry);
  3. Counter global = Metrics.counter("global");
  4. global.increment();
  5. }

SpringBoot Actuator

上述介绍了Micrometer的一些简单使用,从Spring Boot2.0开始,Micrometer就是Spring Boot默认提供的性能指标收集库。SpringBoot Actuator提供了对Micrometer的自动配置。在项目中引入SpringBoot Actuator,

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-actuator</artifactId>
  4. </dependency>

并在配置文件中,增加如下配置
Actuator可对外默认的服务,*表示显示所有

  1. management.endpoints.web.exposure.include=*

启动项目,访问http://8080/actuator,就可以看到Actuator提供的所有监控

  1. {
  2. "_links": {
  3. "self": {
  4. "href": "http://localhost:8080/actuator",
  5. "templated": false
  6. },
  7. "auditevents": {
  8. "href": "http://localhost:8080/actuator/auditevents",
  9. "templated": false
  10. },
  11. "beans": {
  12. "href": "http://localhost:8080/actuator/beans",
  13. "templated": false
  14. },
  15. "caches-cache": {
  16. "href": "http://localhost:8080/actuator/caches/{cache}",
  17. "templated": true
  18. },
  19. "caches": {
  20. "href": "http://localhost:8080/actuator/caches",
  21. "templated": false
  22. },
  23. "health": {
  24. "href": "http://localhost:8080/actuator/health",
  25. "templated": false
  26. },
  27. "health-component": {
  28. "href": "http://localhost:8080/actuator/health/{component}",
  29. "templated": true
  30. },
  31. "health-component-instance": {
  32. "href": "http://localhost:8080/actuator/health/{component}/{instance}",
  33. "templated": true
  34. },
  35. "conditions": {
  36. "href": "http://localhost:8080/actuator/conditions",
  37. "templated": false
  38. },
  39. "configprops": {
  40. "href": "http://localhost:8080/actuator/configprops",
  41. "templated": false
  42. },
  43. "env": {
  44. "href": "http://localhost:8080/actuator/env",
  45. "templated": false
  46. },
  47. "env-toMatch": {
  48. "href": "http://localhost:8080/actuator/env/{toMatch}",
  49. "templated": true
  50. },
  51. "info": {
  52. "href": "http://localhost:8080/actuator/info",
  53. "templated": false
  54. },
  55. "loggers": {
  56. "href": "http://localhost:8080/actuator/loggers",
  57. "templated": false
  58. },
  59. "loggers-name": {
  60. "href": "http://localhost:8080/actuator/loggers/{name}",
  61. "templated": true
  62. },
  63. "heapdump": {
  64. "href": "http://localhost:8080/actuator/heapdump",
  65. "templated": false
  66. },
  67. "threaddump": {
  68. "href": "http://localhost:8080/actuator/threaddump",
  69. "templated": false
  70. },
  71. "prometheus": {
  72. "href": "http://localhost:8080/actuator/prometheus",
  73. "templated": false
  74. },
  75. "metrics": {
  76. "href": "http://localhost:8080/actuator/metrics",
  77. "templated": false
  78. },
  79. "metrics-requiredMetricName": {
  80. "href": "http://localhost:8080/actuator/metrics/{requiredMetricName}",
  81. "templated": true
  82. },
  83. "scheduledtasks": {
  84. "href": "http://localhost:8080/actuator/scheduledtasks",
  85. "templated": false
  86. },
  87. "httptrace": {
  88. "href": "http://localhost:8080/actuator/httptrace",
  89. "templated": false
  90. },
  91. "mappings": {
  92. "href": "http://localhost:8080/actuator/mappings",
  93. "templated": false
  94. }
  95. }
  96. }

访问http://localhost:8080/actuator/metrics,可以看到Actuator默认收集的监控指标,包括JVM相关指标(内存使用,垃圾收集),tomcat相关指标,数据库连接池还是系统相关指标

  1. {
  2. "names": [
  3. "jvm.memory.max",
  4. "jvm.threads.states",
  5. "process.files.max",
  6. "jvm.gc.memory.promoted",
  7. "system.load.average.1m",
  8. "jvm.memory.used",
  9. "jvm.gc.max.data.size",
  10. "jvm.gc.pause",
  11. "jvm.memory.committed",
  12. "system.cpu.count",
  13. "logback.events",
  14. "tomcat.global.sent",
  15. "jvm.buffer.memory.used",
  16. "tomcat.sessions.created",
  17. "jvm.threads.daemon",
  18. "system.cpu.usage",
  19. "jvm.gc.memory.allocated",
  20. "tomcat.global.request.max",
  21. "tomcat.global.request",
  22. "tomcat.sessions.expired",
  23. "jvm.threads.live",
  24. "jvm.threads.peak",
  25. "tomcat.global.received",
  26. "process.uptime",
  27. "tomcat.sessions.rejected",
  28. "process.cpu.usage",
  29. "http.server.requests",
  30. "tomcat.threads.config.max",
  31. "jvm.classes.loaded",
  32. "jvm.classes.unloaded",
  33. "tomcat.global.error",
  34. "tomcat.sessions.active.current",
  35. "tomcat.sessions.alive.max",
  36. "jvm.gc.live.data.size",
  37. "tomcat.threads.current",
  38. "process.files.open",
  39. "jvm.buffer.count",
  40. "jvm.buffer.total.capacity",
  41. "tomcat.sessions.active.max",
  42. "tomcat.threads.busy",
  43. "process.start.time"
  44. ]
  45. }

可以通过以下链接来查看具体某个指标
http://localhost:8080/actuator/metrics/metricName
其中metricName为需要查看指标的名称,例如查看jvm内存
http://localhost:8080/actuator/metrics/jvm.memory.used
从上图中可以看到jvm.memory.used有两个tag,area和id,area指定内存位置(堆内存和非堆内存),id指定内存分类,可以指定tag来查看更细致的指标
http://localhost:8080/actuator/metrics/jvm.memory.used?tag=area:heap
http://localhost:8080/actuator/metrics/jvm.memory.used?tag=area:heap&tag=id:PS%20Eden%20Space

Prometheus

Micrometer支持Prometheus,Micrometer提供PrometheusMeterRegistry注册表,用于将指标转为Prometheus格式的指标。首先需要在pom文件引入依赖

  1. <dependency>
  2. <groupId>io.micrometer</groupId>
  3. <artifactId>micrometer-registry-prometheus</artifactId>
  4. </dependency>

其次在配置文件中,配置暴露Prometheus,并允许将指标导入到Prometheus中

  1. management.endpoint.prometheus.enabled=true
  2. management.metrics.export.prometheus.enabled=true

项目启动后,访问http://localhost:8080/actuator/prometheus,可以看到指标以变成Prometheus格式的指标
可以安装Prometheus来采集这些指标

  1. docker run -d -p 9090:9090 -v ~/Documents/config/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

其中prometheus.yml配置了采集地址及路径

  1. scrape_configs:
  2. - job_name: prometheus-test
  3. metrics_path: /actuator/prometheus
  4. static_configs:
  5. - targets: ['172.16.22.50:8080']

172.16.22.50是本机的地址,可以修改为自己的ip地址即可,访问http://localhost:9090/targets可以看到Prometheus采集配置

自定义Metric

可以利用Prometheus client自定义metric

  1. import io.prometheus.client.CollectorRegistry;
  2. import io.prometheus.client.Counter;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import javax.annotation.PostConstruct;
  6. @Service
  7. public class PrometheusMeter {
  8. @Autowired
  9. private CollectorRegistry collectorRegistry;
  10. // 定义name为prometheus_counter的counter
  11. public Counter prometheusCounter(){
  12. return Counter.build().name("prometheus_counter").help("prometheus counter test")
  13. .register(collectorRegistry);
  14. }
  15. @PostConstruct
  16. public void init(){
  17. Counter counter = prometheusCounter();
  18. new Thread(()-> {
  19. while (true){
  20. counter.inc();
  21. try {
  22. Thread.sleep(5000);
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }).start();
  28. }
  29. }

启动项目之后,可以在Prometheus查询页面看到刚刚定义的指标prometheus_counter

总结

  1. Micrometer整合了多个监控系统,包括Prometheus。Micrometer利用Meter收集数据,利用不同的MeterRegistry与不同的监控系统整合
  2. SpringBoot Actuator集成了Micrometer,定义了许多默认的metric,可以在http://localhost:8080/actuator/metrics查看
  3. SpringBoot Actuator可以通过Micrometer将采集的指标导入到Prometheus中