1. Micrometer简介

Micrometer为Java平台上的性能数据收集提供了一个通用的API,它提供了多种度量指标类型(Timers、Guauges、Counters等),同时支持接入不同的监控系统,例如Influxdb、Graphite、Prometheus等。可以通过Micrometer收集Java性能数据,配合Prometheus监控系统实时获取数据,并最终在Grafana上展示出来,从而很容易实现应用的监控。

Micrometer中有两个最核心的概念,分别是计量器(Meter)和计量器注册表(MeterRegistry)。计量器用来收集不同类型的性能指标信息,Micrometer提供了如下几种不同类型的计量器:

  • 计数器(Counter): 表示收集的数据是按照某个趋势(增加/减少)一直变化的,也是最常用的一种计量器,例如接口请求总数、请求错误总数、队列数量变化等。
  • 计量仪(Gauge): 表示搜集的瞬时的数据,可以任意变化的,例如常用的CPU Load、Memory使用量、Network使用量、实时在线人数统计等,
  • 计时器(Timer): 用来记录事件的持续时间,这个用的比较少。
  • 分布概要(Distribution summary): 用来记录事件的分布情况,表示一段时间范围内对数据进行采样,可以用于统计网络请求平均延迟、请求延迟占比等。

springboot集成Micrometer

  • pom.xml依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.olive</groupId>
 <artifactId>prometheus-demo</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.3.7.RELEASE</version>
  <relativePath />
 </parent>
 <properties>
  <java.version>1.8</java.version>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
 </properties>
 <dependencies>
  <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
  <!-- Micrometer Prometheus registry  -->
  <dependency>
   <groupId>io.micrometer</groupId>
   <artifactId>micrometer-registry-prometheus</artifactId>
  </dependency>
 </dependencies>
 <dependencyManagement>
  <dependencies>
   <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>${spring-boot.version}</version>
    <type>pom</type>
    <scope>import</scope>
   </dependency>
  </dependencies>
 </dependencyManagement>
</project>
  • application.yml配置
#添加以下配置用于暴露指标
management:
  endpoints:
    web:
      exposure:
        # 所有端点都对外暴露可访问到
        include: '*'
  metrics:
    tags:
      application: ${spring.application.name}
    
spring:
  application:
    name: prometheus-demo

management.endpoints.web.exposure.include配置为开启Actuator服务,因为Spring Boot Actuator会自动配置一个URL为/actuator/Prometheus的HTTP服务来供Prometheus抓取数据,不过默认该服务是关闭的,该配置将打开所有的Actuator服务。management.metrics.tags.application配置会将该工程应用名称添加到计量器注册表的tag中去,方便后边 Prometheus 根据应用名称来区分不同的服务。

  • springboot启动引导类
package com.olive;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

 public static void main(String[] args) {
  SpringApplication.run(Application.class, args);
 }

}
  • 注册MeterRegistryCustomizer

其实引入了依赖,通过Actuator暴露你需要暴露的端点之后,对应的Micrometer就会自动配置。但是为了方便监控,建议我们对应用服务配置一些特殊的tag来更加准确的描述我们的监控实例:

package com.olive.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.micrometer.core.instrument.MeterRegistry;

@Configuration
public class MicrometerConfig {

    @Value("${spring.application.name}")
    private String applicationName;

    @Bean
    public MeterRegistryCustomizer configurer(MeterRegistry meterRegistry) {
        return registry -> meterRegistry
          .config()
          .commonTags("application", applicationName, "instance", applicationName + "_instance");
    }

}

以上的tag组合可以更好的来标识你的应用,特别在集群当中。

  • 定义测试接口
package com.olive.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {
 
  Logger logger = LoggerFactory.getLogger(TestController.class);

  @GetMapping("/test")
  public String test() {
    logger.info("test");
    return "ok";
  }

  @GetMapping("/")
  public String home() {
    logger.info("home");
    return "ok";
  }
}
  • 启动服务并访问
http://localhost:8080/actuator/prometheus



Spring Boot应用如何快速接入Prometheus监控_prometheus

访问以下接口

http://localhost:8080/
http://localhost:8080/test

再次访问

http://localhost:8080/actuator/prometheus



Spring Boot应用如何快速接入Prometheus监控_java_02

安全措施

如果上述请求接口不做任何安全限制,安全隐患显而易见。实际上Spring Boot也提供了安全限制功能。比如要禁用/env接口,则可设置如下:

endpoint:
  env:
    enabled: false

另外也可以引入spring-boot-starter-security依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

在application.properties中开启security功能,配置访问权限验证,这时再访问actuator功能时就会弹出登录窗口,需要输入账号密码验证后才允许访问。

spring:
 security:
    user:
      name: admin
      password: abc321

为了只对actuator功能做权限验证,其他应用接口不做认证,需要可以重新定制下SpringSecurity。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.util.StringUtils;

@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig extends WebSecurityConfigurerAdapter {

 @Autowired
 private Environment env;

 @Override
 protected void configure(HttpSecurity security) throws Exception {
  String monitorBasePath = env.getProperty("management.endpoints.web.base-path");
  String contextPath = env.getProperty("server.servlet.context-path");
  if(contextPath==null) {
   contextPath = "";
  }
  if(contextPath.endsWith("/")) {
   contextPath = contextPath.substring(0, contextPath.length()-1);
  }
  if (!StringUtils.isEmpty(monitorBasePath)) {
   if(!monitorBasePath.startsWith("/")) {
    monitorBasePath = "/" + monitorBasePath;
   }
   contextPath = contextPath + monitorBasePath + "/prometheus";
  }else {
   contextPath = contextPath + "/actuator/prometheus";
  }
  security.csrf().disable();
  security.authorizeRequests()
   .antMatchers(contextPath, monitorBasePath + "/prometheus")
   .authenticated()
   .anyRequest()
   .permitAll()
   .and()
   .httpBasic();
 }
}

management.endpoints.web.base-pathserver.servlet.context-path对应配置



Spring Boot应用如何快速接入Prometheus监控_java_03

再次访问此时需要进行权限验证。



Spring Boot应用如何快速接入Prometheus监控_prometheus_04

最终配置

#添加以下配置用于暴露指标
management:
  endpoints:
    web:
      exposure:
        include: '*'
      # actuator的访问路径,替换默认/actuator
      base-path: /monitor
  metrics:
    tags:
      application: ${spring.application.name}
  server:
    #新开监控端口,不和应用用同一个端口    
    port: 9595
    
spring:
  application:
    name: prometheus-demo
  security:
    user:
      password: 123456
      name: admin

server:
  port: 8090
  servlet:
    context-path: /prometheus-demo