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
访问以下接口
http://localhost:8080/ http://localhost:8080/test
再次访问
http://localhost:8080/actuator/prometheus
安全措施
如果上述请求接口不做任何安全限制,安全隐患显而易见。实际上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-path
与server.servlet.context-path
对应配置
再次访问此时需要进行权限验证。
最终配置
#添加以下配置用于暴露指标 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