本文會來看一下Spring Boot Actuator提供給咱們的監控端點Endpoint、健康檢查Health和打點指標Metrics等所謂的Production-ready(生產環境必要的一些)功能。java
咱們先來新建一個模塊:git
<?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啓動器用於測試一些功能。 而後建立主程序:github
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:web
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:redis
package me.josephzhu.spring101ops;
import lombok.AllArgsConstructor;
import lombok.Data;
@AllArgsConstructor
@Data
public class MyItem {
private String name;
private Integer price;
}
複製代碼
最後配置一下application.properties:spring
management.server.port=8081
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
複製代碼
這裏作了幾個配置:數據庫
啓動程序訪問以下地址http://localhost:8081/actuator,能夠看到頁面列出了支持功能的連接,經常使用的有:apache
還有一些其它的自帶的端點,這裏就不說了,好比用於數據遷移的flyway,用於給prometheus拉取metrics數據的端點,端點還能夠自定義。 別小看這些功能,這些功能使得咱們線上對應用程序進行運維能夠很方便:後端
先來訪問/actuator/health重點看一下健康檢查: api
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的狀況:
在本節中,咱們來看一下如何進行簡單配置實現暴露以下應用信息的功能:
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獲取信息的來源:
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能夠看到下面的信息:
<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):
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):
咱們再來看一下如何自定義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:github.com/JosephZhu19…