閱讀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。