阅读PDF版本

本文会来看一下Spring Boot Actuator提供给我们的监控端点Endpoint、健康检查Health和打点指标Metrics等所谓的Production-ready(生产环境必要的一些)功能。

监控端点

我们先来新建一个模块:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <groupId>me.josephzhu</groupId>
  6. <artifactId>spring101-ops</artifactId>
  7. <version>0.0.1-SNAPSHOT</version>
  8. <packaging>jar</packaging>
  9. <name>spring101-ops</name>
  10. <description>Demo project for Spring Boot</description>
  11. <parent>
  12. <groupId>me.josephzhu</groupId>
  13. <artifactId>spring101</artifactId>
  14. <version>0.0.1-SNAPSHOT</version>
  15. </parent>
  16. <dependencies>
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-starter-web</artifactId>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework.boot</groupId>
  23. <artifactId>spring-boot-starter-data-redis</artifactId>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework.boot</groupId>
  27. <artifactId>spring-boot-starter-actuator</artifactId>
  28. </dependency>
  29. </dependencies>
  30. </project>

引入了必要的actuator和web启动器,此外,还引入了data-redis启动器用于测试一些功能。

然后创建主程序:

  1. package me.josephzhu.spring101ops;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.boot.web.client.RestTemplateBuilder;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.web.client.RestTemplate;
  8. @SpringBootApplication
  9. @Configuration
  10. public class Spring101OpsApplication {
  11. public static void main(String[] args) {
  12. SpringApplication.run(Spring101OpsApplication.class, args);
  13. }
  14. @Bean
  15. RestTemplate restTemplate(RestTemplateBuilder builder){
  16. return builder.build();
  17. }
  18. }

创建一个测试Controller:

  1. package me.josephzhu.spring101ops;
  2. import io.micrometer.core.instrument.MeterRegistry;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.data.redis.core.StringRedisTemplate;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RequestParam;
  8. import org.springframework.web.bind.annotation.RestController;
  9. import java.util.List;
  10. import java.util.stream.Collectors;
  11. import java.util.stream.IntStream;
  12. @RestController
  13. @RequestMapping("api")
  14. public class MyController {
  15. @Autowired
  16. StringRedisTemplate stringRedisTemplate;
  17. @GetMapping("items")
  18. public List<MyItem> items(@RequestParam(value = "count",defaultValue = "10") int count){
  19. stringRedisTemplate.opsForValue().set("testKey", "value" + count);
  20. return IntStream.rangeClosed(1,count).mapToObj(i->new MyItem("name" + i,i)).collect(Collectors.toList());
  21. }
  22. }

这里有用到一个MyItem:

  1. package me.josephzhu.spring101ops;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. @AllArgsConstructor
  5. @Data
  6. public class MyItem {
  7. private String name;
  8. private Integer price;
  9. }

最后配置一下application.properties:

  1. management.server.port=8081
  2. management.endpoints.web.exposure.include=*
  3. management.endpoint.health.show-details=always

这里做了几个配置:

  1. 修改actuator的访问端口为8081
  2. 开放所有的访问端点,引入Spring Security后可以对暴露的断点进行更多安全配置,生产环境不建议直接开启所有端点
  3. 健康检查端点显示详细信息,如果不展开显示详细信息,那么只会有一个总的状态信息

启动程序访问如下地址http://localhost:8081/actuator,可以看到页面列出了支持功能的链接,常用的有:

  1. auditevents:查看审计事件
  2. beans:查看Spring中的Bean清单

  3. conditions:查看Spring Boot自动配置匹配过程

  4. configprops:查看所有的配置

  5. env:查看Spring的ConfigurableEnvironment

  6. health:查看程序健康情况,后面细说
  7. httptrace:查看HTTP请求响应明细(默认100条)

  8. info:查看程序信息,后面细说
  9. loggers:查看日志配置,支持动态修改日志级别

  10. metrics:查看所有的metrics信息,后面细说
  11. mappings:查看MVC的@RequestMapping配置

  12. scheduledtasks:查看定时任务
  13. sessions:查看和删除用户session
  14. shutdown:优雅停止程序
  15. threaddump:线程Dump

  16. heapdump:下载GZIP压缩的hprof文件
  17. logfile:查看日志文件,需要先配置日志文件路径

还有一些其它的自带的端点,这里就不说了,比如用于数据迁移的flyway,用于给prometheus拉取metrics数据的端点,端点还可以自定义。

别小看这些功能,这些功能使得我们线上对应用程序进行运维可以很方便:

  1. 可以排查配置、环境问题
  2. 可以对线程Dump排查High CPU问题
  3. 可以对堆Dump排查Memory Leak问题
  4. 可以修改日志级别,排查程序问题
  5. 可以查看定时任务、RequestMapping等信息
  6. 可以让负载均衡器有统一的端口检查应用状态
  7. 可以暴露、收集、汇总程序内部的各种指标用于绘制监控面板
  8. 其它各种程序内部的信息

健康检查

先来访问/actuator/health重点看一下健康检查:



这里可以看到不但Spring Boot帮我们自动配置收集了redis和磁盘的监控信息,而且我们还自定义了一个叫做myService的检查项。这里因为我们程序有使用到Redis,所以自动配置了相应的检查,自动支持自动配置的HealthIndicator有下面这些(基本各种Spring Data支持的数据源都有了):



下面我们看一下如何自定义监控检查项:

  1. package me.josephzhu.spring101ops;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.boot.actuate.health.Health;
  4. import org.springframework.boot.actuate.health.HealthIndicator;
  5. import org.springframework.core.ParameterizedTypeReference;
  6. import org.springframework.http.HttpMethod;
  7. import org.springframework.http.ResponseEntity;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.web.client.RestTemplate;
  10. import java.util.List;
  11. @Component
  12. public class MyServiceHealthIndicator implements HealthIndicator {
  13. @Autowired
  14. private RestTemplate restTemplate;
  15. @Override
  16. public Health health() {
  17. try {
  18. ResponseEntity<List<MyItem>> responseEntity =
  19. restTemplate.exchange("http://localhost:8080/api/items",
  20. HttpMethod.GET, null, new ParameterizedTypeReference<List<MyItem>>() {});
  21. if (responseEntity.getStatusCode().is2xxSuccessful())
  22. return Health.up().build();
  23. else
  24. return Health.down().status(responseEntity.getStatusCode().toString()).build();
  25. } catch (Exception ex) {
  26. return Health.down(ex).build();
  27. }
  28. }
  29. }

实现很简单,实现HealthIndicator接口即可,我们的类是XXHealthIndicator,那么自动就会加入一个叫XX的项。在这里,我们访问了远程的一个服务,当服务出现非2XX的响应或调用服务出现异常的时候,我们认为这个服务的健康是DOWN的,否则就是UP状态。在down的时候我们可以传入状态、异常等补充信息,比如这是一个DOWN的情况:



因为某一项DOWN了,整个应用的状态认为是DOWN。

之前也说过,如果每一个程序都有统一的health入口可以监控程序状态的话,我们的负载均衡就可以依靠这个来做应用的故障下线。之所以我们需要定义自己的HealthIndicator加入是因为很多时候程序启动成功并不代表程序正常工作,程序是否真正工作正常往往依赖于外部的一些关键服务和内部的一些关键组件(线程池、队列等)。

应用信息

在本节中,我们来看一下如何进行简单配置实现暴露如下应用信息的功能:

  1. 通过配置方式暴露自定义的信息
  2. 通过程序方式暴露自定义的信息
  3. 暴露应用构建信息
  4. 暴露应用git版本信息

    访问/actuator/info可以看到下面的信息:



    在截图中第一项可以看到app这个JSON暴露了一些自定义的信息,我们可以在application.properties配置文件中加入这样的配置来实现:
  1. info.app.name=Test SpringBoot Actuator
  2. info.app.description=Test how to configure Health/Info/Metrics etc. with Spring Boot Actuator
  3. info.app.version=1.0.1
  4. info.app.author=JosephZhu

截图中第二项可以看到git这个JSON暴露了程序git相关的信息,实现方式是加入一个插件到pom中:

  1. <plugin>
  2. <groupId>pl.project13.maven</groupId>
  3. <artifactId>git-commit-id-plugin</artifactId>
  4. <version>2.1.15</version>
  5. </plugin>

此外,如果需要查看git详细信息的话需要加入如下配置到配置文件:

  1. management.info.git.mode=full

截图中第三项可以看到build这个JSON暴露了程序构建一些信息,这个通过Spring Boot的maven插件就可以实现,加入build-info这个Goal来生成:

  1. <plugin>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-maven-plugin</artifactId>
  4. <executions>
  5. <execution>
  6. <goals>
  7. <goal>build-info</goal>
  8. </goals>
  9. </execution>
  10. </executions>
  11. </plugin>

我们使用maven运行一下这两个插件,可以看到编译后多了build-info.properties和git.properties,这也就是Actuator获取信息的来源:



截图中最后我们看到有一个key项的信息,这是我们通过程序定义的,实现方式是实现InfoContributor接口:

  1. package me.josephzhu.spring101ops;
  2. import org.springframework.boot.actuate.info.Info;
  3. import org.springframework.boot.actuate.info.InfoContributor;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. public class MyInfoContributor implements InfoContributor {
  7. @Override
  8. public void contribute(Info.Builder builder) {
  9. builder.withDetail("key","value").build();
  10. }
  11. }

对外暴露一些程序内部的重要信息往往也是很重要排查线上问题的手段。Info适合展示不太会变动的一些复杂信息,如果希望整个历史状态信息都能保留和监控的话更适合采用下面的打点方式。

监控打点

访问/actuator/metrics可以看到下面的信息:



Spring Boot集成了一个叫做MicroMeter的库用于Metrics。这个库号称是Metrics界的SLF4J,它的作用在于:

  1. 可以适配多达10+的监控数据库,包括Graphite、Influx、StatsD等等。
  2. 以统一的语言定义了打点这个事情。
  3. 自动集成了很多JVM的信息,包括内存、GC、CPU、线程等。

    我们可以随便点一个Metrics进去查看:



    下面,我们来看一下如何通过简单的几个配置实现把所有的打点信息发送到InfluxDb中去。

    第一步,在pom中引入influx依赖:
  1. <dependency>
  2. <groupId>io.micrometer</groupId>
  3. <artifactId>micrometer-registry-influx</artifactId>
  4. </dependency>

当然,需要先在本机安装一下influxdb,MacOS也就是一行命令,brew install influxdb即可。

第二步,加入配置:

  1. management.metrics.web.server.auto-time-requests=true
  2. management.metrics.export.influx.enabled=true
  3. management.metrics.export.influx.auto-create-db=true
  4. management.metrics.export.influx.db=myapp
  5. management.metrics.export.influx.step=10s

逐一说明一下这些配置:

  1. 第一个配置用于自动开启所有Web请求的执行时间记录,如果不这么配的话可以手动在Controller上加@Timed注解
  2. 第二个配置用于开启micrometer的influx的支持
  3. 第三个配置用于自动创建influxdb的数据库
  4. 第四个配置指定了influxdb的数据库名称为myapp
  5. 第五个配置指定了汇总上报数据到influxdb的周期

我们可以启动程序,多访问几次http://localhost:8080/api/items,然后打开http://localhost:8081/actuator/metrics/http.server.requests查看,可以看到具体请求的执行次数和执行时间(证明1):



同时,我们可以在日志中看到(证明5):

  1. 2018-10-08 20:49:41.429 INFO 16390 --- [pool-1-thread-1] i.micrometer.influx.InfluxMeterRegistry : successfully sent 73 metrics to influx
  2. 2018-10-08 20:49:51.483 INFO 16390 --- [pool-1-thread-1] i.micrometer.influx.InfluxMeterRegistry : successfully sent 73 metrics to influx

有73个监控指标每隔10秒发送到一次influxdb,我们可以进入influx客户端,查看这个measurement的信息(先输入命令use myapp):



还记得吗,我们实现了一个自定义的HealthIndicator,里面有用到RestTemplate来访问远程服务,现在可以尝试多访问几次http://localhost:8081/actuator/health,然后打开http://localhost:8081/actuator/metrics/http.client.requests可以看到的确有信息:



说明RestTemplat默认为我们集成了Metrics,记录客户端HTTP请求的执行情况。

数据收集和上发的工作完成了,最后我们可以brew install grafana,使用Grafna连接InfluxDb来配置一下我们的监控面板。

首先配置一下数据源:



然后就可以配置图表了:



有关Grafna的使用方式这里不详述了。

自定义监控打点

我们再来看一下如何自定义Metrics项实现自己的打点,修改一下我们的Controller:

  1. @Autowired
  2. MeterRegistry meterRegistry;
  3. @GetMapping("items")
  4. public List<MyItem> items(@RequestParam(value = "count",defaultValue = "10") int count){
  5. stringRedisTemplate.opsForValue().set("testKey", "value" + count);
  6. meterRegistry.timer("mytimer").record(()-> {
  7. try {
  8. Thread.sleep(count);
  9. } catch (InterruptedException e) {
  10. }
  11. });
  12. meterRegistry.counter("mycounter").increment(count);
  13. meterRegistry.gauge("currentValue1", count);
  14. return IntStream.rangeClosed(1,count).mapToObj(i->new MyItem("name" + i,i)).collect(Collectors.toList());
  15. }

在这里,我们注入了MeterRegistry类,然后通过这个类我们可以方便进行各种信息的上报。在MicroMeter中,信息抽象为了这么几种:

  1. 状态信息,所谓状态信息就是每次上报的是信息的当前值,数据是不能累计的。比如当前线程数48,1分钟后是50,这个值如果进行汇总聚合的话没有太大意义(总计98个线程?平均49个线程?),在展现的时候只能展现某个时刻的某个值。

  2. 数量信息,比如请求执行的次数,一次上报可以+1也可以+N,这个数据是可以聚合的。比如如果我们在1分钟内上报了2次,一次10,一次20,那么1分钟的聚合就是总数30,每次上报记录的是聚合的总和信息。

  3. 执行时间信息,可以方便的统计方法执行时间。上报的信息会比数量信息丰富,除了次数之外还有总执行时间、平均执行时间、最长执行时间。



    是不是很方便,使用MicroMeter统一的API,我们只要根据需要收集指标数据即可,剩下的数据整理、汇总以及和后端数据库交互上报数据的过程都自动完成。

    最后,我们再来看一个把/actuator/health的信息转换成打点上报的例子,我们可以通过获得所有的HealthIndicator,交由CompositeHealthIndicator汇总健康信息结果,然后通过MeterRegistry上报监控信息的打点:
  1. package me.josephzhu.spring101ops;
  2. import io.micrometer.core.instrument.MeterRegistry;
  3. import org.springframework.boot.actuate.health.CompositeHealthIndicator;
  4. import org.springframework.boot.actuate.health.HealthAggregator;
  5. import org.springframework.boot.actuate.health.HealthIndicator;
  6. import org.springframework.boot.actuate.health.Status;
  7. import org.springframework.context.annotation.Configuration;
  8. import java.util.List;
  9. import static java.util.Collections.emptyList;
  10. @Configuration
  11. class HealthMetricsConfiguration {
  12. private CompositeHealthIndicator compositeHealthIndicator;
  13. public HealthMetricsConfiguration(HealthAggregator healthAggregator,
  14. List<HealthIndicator> healthIndicators,
  15. MeterRegistry registry) {
  16. compositeHealthIndicator = new CompositeHealthIndicator(healthAggregator);
  17. for (Integer i = 0; i < healthIndicators.size(); i++) {
  18. compositeHealthIndicator.addHealthIndicator(i.toString(), healthIndicators.get(i));
  19. }
  20. registry.gauge("health", emptyList(), compositeHealthIndicator, health -> {
  21. Status status = health.health().getStatus();
  22. switch (status.getCode()) {
  23. case "UP":
  24. return 3;
  25. case "OUT_OF_SERVICE":
  26. return 2;
  27. case "DOWN":
  28. return 1;
  29. case "UNKNOWN":
  30. default:
  31. return 0;
  32. }
  33. });
  34. }
  35. }

重启应用后稍等一下,看一下InfluxDb中的数据,的确是一些值为3的记录,代表UP:

总结

本文我们通过一些例子覆盖了如下内容:

  1. Actuator模块的功能。
  2. 详述了健康检查端点,自定义健康检查项。
  3. 详述了数据信息端点,为程序添加各种附加数据项。
  4. 详述了监控打点,程序方式上报自定义的三种形式的打点。
  5. InfluxDb和Grafana的简单使用。

的确,每一个功能都是简单的几步配置,Spring Boot Actuator真的很方便,这些功能是一个真正的可用于生产环境的程序必不可少的一些功能,Spring Boot不仅仅为我们提供了方便,而且为我们定义了架构模板,让每一个开发人员都能有意识,应该做一些什么,这也就是我为什么一直说Spring引领了企业级单机开发,Spring Boot引领了互联网微服务开发。

但是,Spring Boot因为在高速发展,会不断吸收好的开源项目整合到生态中去,所以在API上变化会较多,API一直在修改,增加了不少学习成本和坑。任何事情都有两面性,我们在要求Spring生态为我们提供越来越多功能的时候,享受到便利的同时,也必须去适应Spring的快速变化。

老样子,本系列文章代码见我的github:https://github.com/JosephZhu1983/Spring101。

朱晔和你聊Spring系列S1E7:简单好用的Spring Boot Actuator的更多相关文章

  1. Spring系列9:基于注解的Spring容器配置

    写在前面 前面几篇中我们说过,Spring容器支持3种方式进行bean定义信息的配置,现在具体说明下: XML:bean的定义和依赖都在xml文件中配置,比较繁杂. Annotation-based ...

  2. Spring 系列: Spring 框架简介 -7个部分

    Spring 系列: Spring 框架简介 Spring AOP 和 IOC 容器入门 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级 ...

  3. Spring 系列: Spring 框架简介(转载)

    Spring 系列: Spring 框架简介 http://www.ibm.com/developerworks/cn/java/wa-spring1/ Spring AOP 和 IOC 容器入门 在 ...

  4. [JavaEE] IBM - Spring 系列: Spring 框架简介

    Spring AOP 和 IOC 容器入门 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级的.强壮的 J2EE 应用程序.develop ...

  5. Spring 系列: Spring 框架简介

    Spring AOP 和 IOC 容器入门(转载) 在这由三部分组成的介绍 Spring 框架的系列文章的第一期中,将开始学习如何用 Spring 技术构建轻量级的.强壮的 J2EE 应用程序.dev ...

  6. 朱晔和你聊Spring系列S1E2:SpringBoot并不神秘

    朱晔和你聊Spring系列S1E2:SpringBoot并不神秘 [编辑器丢失了所有代码的高亮,建议查看PDF格式文档] 文本我们会一步一步做一个例子来看看SpringBoot的自动配置是如何实现的, ...

  7. 朱晔和你聊Spring系列S1E1:聊聊Spring家族的几大件

    朱晔和你聊Spring系列S1E1:聊聊Spring家族的几大件 [下载本文PDF进行阅读] Spring家族很庞大,从最早先出现的服务于企业级程序开发的Core.安全方面的Security.到后来的 ...

  8. 朱晔和你聊Spring系列S1E11:小测Spring Cloud Kubernetes @ 阿里云K8S

    有关Spring Cloud Kubernates(以下简称SCK)详见https://github.com/spring-cloud/spring-cloud-kubernetes,在本文中我们主要 ...

  9. 朱晔和你聊Spring系列S1E8:凑活着用的Spring Cloud(含一个实际业务贯穿所有组件的完整例子)

    本文会以一个简单而完整的业务来阐述Spring Cloud Finchley.RELEASE版本常用组件的使用.如下图所示,本文会覆盖的组件有: Spring Cloud Netflix Zuul网关 ...

随机推荐

  1. Javascript数组系列二之迭代方法1

    我们在<Javascript数组系列一之栈与队列 >中介绍了一些数组的用法.比如:数组如何表现的和「栈」一样,用什么方法表现的和「队列」一样等等一些方法,因为 Javascript 中的数 ...

  2. Ext 日期格式化

    //日期格式化 Date.prototype.Format = function (fmt) { var o = { , //月份 "d+": this.getDate(), // ...

  3. SQL Server中如何定位Row Lock锁定哪一行数据

    在SQL Server中有时候会使用提示(Hint)强制SQL使用行锁(Row Lock),前两天有个同事咨询了一个问题,如何定位Row Lock具体锁定了哪一行.其实这个问题只适合研究一下,实际意义 ...

  4. [20170612]FOR ALL COLUMNS SIZE repeat(11g).txt

    [20170612]FOR ALL COLUMNS SIZE repeat(11g).txt --//昨天看了https://jonathanlewis.wordpress.com/2017/06/0 ...

  5. no plugin found for prefix 'tomcat 7' in the current project and in the plugin groups的解决方法

    解决方法一: 找到这个settings.xml文件,进行编辑,在pluginGroups标签下加入下面的配置 <pluginGroups><pluginGroup>org.ap ...

  6. Ajax的一个实例及代码

    这是用ajax做的一个小小的应用!当选择menu1的时候,会出来menu里面所有的内容.同理对于menu2.多的不说,代码如下: 首先是inner.html文件 <html><hea ...

  7. 利用Audacity软件分析ctf音频隐写

    分析音频得到摩斯电码 看波的宽度分辨长短音 比较细的就是短音,代表"." 比较粗的就是长音,代表"-" 中间的间隔就是" " 得到摩斯电码

  8. Word中的通配符随意组合进行批量替换或删除某些内容

    长文档需要批量修改或删除某些内容的时候,我们可以利用Word中的通配符来搞定这一切,当然,前提是你必须会使用它.通配符的功能非常强大,能够随意组合替换或删除我们定义的规则内容,下面易老师就分享一些关于 ...

  9. MySQL JDBC驱动版本与MySQL数据库版本对应关系

    前言:前段时间发现在家使用和公司一样的mysql jdbc驱动版本发生了异常,原因:家里mysql数据库版本与公司不一致导致.查询了相关资料,发现mysql jdbc驱动版本与mysql数据库版本有一 ...

  10. Oracle会话超时退出设置

    前一段时间客户打电话说自从数据库搬迁后连接数据库总是提示会话不可用,和客户沟通才知到他们连接数据库的程序是从早上连上数据库后就一直保持连接状态,一天中需要执行几次操作,由于数据库中的会话连接有超时限制 ...