朱晔和你聊Spring系列S1E7:简单好用的Spring Boot Actuator

阅读PDF版本java

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

监控端点

咱们先来新建一个模块:github

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>me.josephzhu</groupId>
   <artifactId>spring101-ops</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>

   <name>spring101-ops</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>me.josephzhu</groupId>
      <artifactId>spring101</artifactId>
      <version>0.0.1-SNAPSHOT</version>
   </parent>


   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
   </dependencies>
</project>

引入了必要的actuator和web启动器,此外,还引入了data-redis启动器用于测试一些功能。
而后建立主程序:web

package me.josephzhu.spring101ops;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@Configuration
public class Spring101OpsApplication {

   public static void main(String[] args) {

      SpringApplication.run(Spring101OpsApplication.class, args);
   }
   @Bean
   RestTemplate restTemplate(RestTemplateBuilder builder){
      return builder.build();
   }
}

建立一个测试Controller:redis

package me.josephzhu.spring101ops;

import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@RestController
@RequestMapping("api")
public class MyController {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @GetMapping("items")
    public List<MyItem> items(@RequestParam(value = "count",defaultValue = "10") int count){
        stringRedisTemplate.opsForValue().set("testKey", "value" + count);
        return IntStream.rangeClosed(1,count).mapToObj(i->new MyItem("name" + i,i)).collect(Collectors.toList());
    }
}

这里有用到一个MyItem:spring

package me.josephzhu.spring101ops;

import lombok.AllArgsConstructor;
import lombok.Data;

@AllArgsConstructor
@Data
public class MyItem {
    private String name;
    private Integer price;
}

最后配置一下application.properties:数据库

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

这里作了几个配置:apache

  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数据的端点,端点还能够自定义。
别小看这些功能,这些功能使得咱们线上对应用程序进行运维能够很方便:api

  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支持的数据源都有了):

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

package me.josephzhu.spring101ops;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@Component
public class MyServiceHealthIndicator implements HealthIndicator {
    @Autowired
    private RestTemplate restTemplate;

    @Override
    public Health health() {
        try {
            ResponseEntity<List<MyItem>> responseEntity =
                    restTemplate.exchange("http://localhost:8080/api/items",
                            HttpMethod.GET, null, new ParameterizedTypeReference<List<MyItem>>() {});
            if (responseEntity.getStatusCode().is2xxSuccessful())
                return Health.up().build();
            else
                return Health.down().status(responseEntity.getStatusCode().toString()).build();

        } catch (Exception ex) {
            return Health.down(ex).build();
        }
    }
}

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

由于某一项DOWN了,整个应用的状态认为是DOWN。
以前也说过,若是每个程序都有统一的health入口能够监控程序状态的话,咱们的负载均衡就能够依靠这个来作应用的故障下线。之因此咱们须要定义本身的HealthIndicator加入是由于不少时候程序启动成功并不表明程序正常工做,程序是否真正工做正常每每依赖于外部的一些关键服务和内部的一些关键组件(线程池、队列等)。

应用信息

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

  1. 经过配置方式暴露自定义的信息
  2. 经过程序方式暴露自定义的信息
  3. 暴露应用构建信息
  4. 暴露应用git版本信息
    访问/actuator/info能够看到下面的信息:

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

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

<plugin>
   <groupId>pl.project13.maven</groupId>
   <artifactId>git-commit-id-plugin</artifactId>
   <version>2.1.15</version>
</plugin>

此外,若是须要查看git详细信息的话须要加入以下配置到配置文件:

management.info.git.mode=full

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

<plugin>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-maven-plugin</artifactId>
   <executions>
      <execution>
         <goals>
            <goal>build-info</goal>
         </goals>
      </execution>
   </executions>
</plugin>

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

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

package me.josephzhu.spring101ops;

import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

@Component
public class MyInfoContributor implements InfoContributor {
    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("key","value").build();
    }
}

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

监控打点

访问/actuator/metrics能够看到下面的信息:

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

  1. 能够适配多达10+的监控数据库,包括Graphite、Influx、StatsD等等。
  2. 以统一的语言定义了打点这个事情。
  3. 自动集成了不少JVM的信息,包括内存、GC、CPU、线程等。
    咱们能够随便点一个Metrics进去查看:

    下面,咱们来看一下如何经过简单的几个配置实现把全部的打点信息发送到InfluxDb中去。
    第一步,在pom中引入influx依赖:
<dependency>
   <groupId>io.micrometer</groupId>
   <artifactId>micrometer-registry-influx</artifactId>
</dependency>

固然,须要先在本机安装一下influxdb,MacOS也就是一行命令,brew install influxdb便可。
第二步,加入配置:

management.metrics.web.server.auto-time-requests=true
management.metrics.export.influx.enabled=true
management.metrics.export.influx.auto-create-db=true
management.metrics.export.influx.db=myapp
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):

2018-10-08 20:49:41.429  INFO 16390 --- [pool-1-thread-1] i.micrometer.influx.InfluxMeterRegistry  : successfully sent 73 metrics to influx
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:

@Autowired
MeterRegistry meterRegistry;

@GetMapping("items")
public List<MyItem> items(@RequestParam(value = "count",defaultValue = "10") int count){
    stringRedisTemplate.opsForValue().set("testKey", "value" + count);

    meterRegistry.timer("mytimer").record(()-> {
        try {
            Thread.sleep(count);
        } catch (InterruptedException e) {
        }
    });
    meterRegistry.counter("mycounter").increment(count);
    meterRegistry.gauge("currentValue1", count);

    return IntStream.rangeClosed(1,count).mapToObj(i->new MyItem("name" + i,i)).collect(Collectors.toList());
}

在这里,咱们注入了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上报监控信息的打点:
package me.josephzhu.spring101ops;

import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.Status;
import org.springframework.context.annotation.Configuration;

import java.util.List;

import static java.util.Collections.emptyList;

@Configuration
class HealthMetricsConfiguration {
    private CompositeHealthIndicator compositeHealthIndicator;

    public HealthMetricsConfiguration(HealthAggregator healthAggregator,
                                      List<HealthIndicator> healthIndicators,
                                      MeterRegistry registry) {

        compositeHealthIndicator = new CompositeHealthIndicator(healthAggregator);

        for (Integer i = 0; i < healthIndicators.size(); i++) {
            compositeHealthIndicator.addHealthIndicator(i.toString(), healthIndicators.get(i));
        }

        registry.gauge("health", emptyList(), compositeHealthIndicator, health -> {
            Status status = health.health().getStatus();
            switch (status.getCode()) {
                case "UP":
                    return 3;
                case "OUT_OF_SERVICE":
                    return 2;
                case "DOWN":
                    return 1;
                case "UNKNOWN":
                default:
                    return 0;
            }
        });
    }
}

重启应用后稍等一下,看一下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。

相关文章
相关标签/搜索