阅读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
启动程序访问以下地址http://localhost:8081/actuator,能够看到页面列出了支持功能的连接,经常使用的有:后端
还有一些其它的自带的端点,这里就不说了,好比用于数据迁移的flyway,用于给prometheus拉取metrics数据的端点,端点还能够自定义。
别小看这些功能,这些功能使得咱们线上对应用程序进行运维能够很方便:api
先来访问/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加入是由于不少时候程序启动成功并不表明程序正常工做,程序是否真正工做正常每每依赖于外部的一些关键服务和内部的一些关键组件(线程池、队列等)。
在本节中,咱们来看一下如何进行简单配置实现暴露以下应用信息的功能:
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,它的做用在于:
<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
逐一说明一下这些配置:
咱们能够启动程序,多访问几回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中,信息抽象为了这么几种:
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:
本文咱们经过一些例子覆盖了以下内容:
的确,每个功能都是简单的几步配置,Spring Boot Actuator真的很方便,这些功能是一个真正的可用于生产环境的程序必不可少的一些功能,Spring Boot不只仅为咱们提供了方便,并且为咱们定义了架构模板,让每个开发人员都能有意识,应该作一些什么,这也就是我为何一直说Spring引领了企业级单机开发,Spring Boot引领了互联网微服务开发。
可是,Spring Boot由于在高速发展,会不断吸取好的开源项目整合到生态中去,因此在API上变化会较多,API一直在修改,增长了很多学习成本和坑。任何事情都有两面性,咱们在要求Spring生态为咱们提供愈来愈多功能的时候,享受到便利的同时,也必须去适应Spring的快速变化。
老样子,本系列文章代码见个人github:https://github.com/JosephZhu1983/Spring101。