朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

本文會來看一下Spring Boot Actuator提供給我們的監控端點Endpoint、健康檢查Health和打點指標Metrics等所謂的Production-ready(生產環境必要的一些)功能。

監控端點

我們先來新建一個模塊:


xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
me.josephzhu
spring101-ops
0.0.1-SNAPSHOT
jar
spring101-ops
Demo project for Spring Boot

me.josephzhu
spring101
0.0.1-SNAPSHOT



org.springframework.boot

spring-boot-starter-web


org.springframework.boot
spring-boot-starter-data-redis


org.springframework.boot
spring-boot-starter-actuator



複製代碼

引入了必要的actuator和web啟動器,此外,還引入了data-redis啟動器用於測試一些功能。 然後創建主程序:

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:

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 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:

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
複製代碼

這裡做了幾個配置:

  1. 修改actuator的訪問端口為8081
  2. 開放所有的訪問端點,引入Spring Security後可以對暴露的斷點進行更多安全配置,生產環境不建議直接開啟所有端點
  3. 健康檢查端點顯示詳細信息,如果不展開顯示詳細信息,那麼只會有一個總的狀態信息

啟動程序訪問如下地址http://localhost:8081/actuator,可以看到頁面列出了支持功能的鏈接,常用的有:

auditevents:查看審計事件

beans:查看Spring中的Bean清單

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

conditions:查看Spring Boot自動配置匹配過程

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

configprops:查看所有的配置

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

env:查看Spring的ConfigurableEnvironment

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

health:查看程序健康情況,後面細說

httptrace:查看HTTP請求響應明細(默認100條)

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

info:查看程序信息,後面細說

loggers:查看日誌配置,支持動態修改日誌級別

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

metrics:查看所有的metrics信息,後面細說

mappings:查看MVC的@RequestMapping配置

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

scheduledtasks:查看定時任務

sessions:查看和刪除用戶session

shutdown:優雅停止程序

threaddump:線程Dump

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

heapdump:下載GZIP壓縮的hprof文件

logfile:查看日誌文件,需要先配置日誌文件路徑

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

還有一些其它的自帶的端點,這裡就不說了,比如用於數據遷移的flyway,用於給prometheus拉取metrics數據的端點,端點還可以自定義。 別小看這些功能,這些功能使得我們線上對應用程序進行運維可以很方便:

  1. 可以排查配置、環境問題
  2. 可以對線程Dump排查High CPU問題
  3. 可以對堆Dump排查Memory Leak問題
  4. 可以修改日誌級別,排查程序問題
  5. 可以查看定時任務、RequestMapping等信息
  6. 可以讓負載均衡器有統一的端口檢查應用狀態
  7. 可以暴露、收集、彙總程序內部的各種指標用於繪製監控面板
  8. 其它各種程序內部的信息

健康檢查

先來訪問/actuator/health重點看一下健康檢查:

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

這裡可以看到不但Spring Boot幫我們自動配置收集了redis和磁盤的監控信息,而且我們還自定義了一個叫做myService的檢查項。這裡因為我們程序有使用到Redis,所以自動配置了相應的檢查,自動支持自動配置的HealthIndicator有下面這些(基本各種Spring Data支持的數據源都有了):

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

下面我們看一下如何自定義監控檢查項: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> responseEntity =
restTemplate.exchange("http://localhost:8080/api/items",
HttpMethod.GET, null, new ParameterizedTypeReference>() {});
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的情況:

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

因為某一項DOWN了,整個應用的狀態認為是DOWN。 之前也說過,如果每一個程序都有統一的health入口可以監控程序狀態的話,我們的負載均衡就可以依靠這個來做應用的故障下線。之所以我們需要定義自己的HealthIndicator加入是因為很多時候程序啟動成功並不代表程序正常工作,程序是否真正工作正常往往依賴於外部的一些關鍵服務和內部的一些關鍵組件(線程池、隊列等)。

應用信息

在本節中,我們來看一下如何進行簡單配置實現暴露如下應用信息的功能:

  1. 通過配置方式暴露自定義的信息
  2. 通過程序方式暴露自定義的信息
  3. 暴露應用構建信息
  4. 暴露應用git版本信息 訪問/actuator/info可以看到下面的信息:
朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

在截圖中第一項可以看到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中:


pl.project13.maven
git-commit-id-plugin
2.1.15

複製代碼

此外,如果需要查看git詳細信息的話需要加入如下配置到配置文件:

management.info.git.mode=full
複製代碼

截圖中第三項可以看到build這個JSON暴露了程序構建一些信息,這個通過Spring Boot的maven插件就可以實現,加入build-info這個Goal來生成:


org.springframework.boot
spring-boot-maven-plugin



build-info




複製代碼

我們使用maven運行一下這兩個插件,可以看到編譯後多了build-info.properties和git.properties,這也就是Actuator獲取信息的來源:

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot 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系列S1E7:簡單好用的Spring Boot Actuator

  1. Spring Boot集成了一個叫做MicroMeter的庫用於Metrics。這個庫號稱是Metrics界的SLF4J,它的作用在於:可以適配多達10+的監控數據庫,包括Graphite、Influx、StatsD等等。
  2. 以統一的語言定義了打點這個事情。
  3. 自動集成了很多JVM的信息,包括內存、GC、CPU、線程等。 我們可以隨便點一個Metrics進去查看:

下面,我們來看一下如何通過簡單的幾個配置實現把所有的打點信息發送到InfluxDb中去。 第一步,在pom中引入influx依賴:


io.micrometer
micrometer-registry-influx

複製代碼

當然,需要先在本機安裝一下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):

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

同時,我們可以在日誌中看到(證明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):

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

還記得嗎,我們實現了一個自定義的HealthIndicator,裡面有用到RestTemplate來訪問遠程服務,現在可以嘗試多訪問幾次http://localhost:8081/actuator/health,然後打開http://localhost:8081/actuator/metrics/http.client.requests可以看到的確有信息:

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

說明RestTemplat默認為我們集成了Metrics,記錄客戶端HTTP請求的執行情況。 數據收集和上發的工作完成了,最後我們可以brew install grafana,使用Grafna連接InfluxDb來配置一下我們的監控面板。 首先配置一下數據源:

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

然後就可以配置圖表了:

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

有關Grafana的使用方式這裡不詳述了。

自定義監控打點

我們再來看一下如何自定義Metrics項實現自己的打點,修改一下我們的Controller:

@Autowired
MeterRegistry meterRegistry;
@GetMapping("items")
public List
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個線程?),在展現的時候只能展現某個時刻的某個值。

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

2、數量信息,比如請求執行的次數,一次上報可以+1也可以+N,這個數據是可以聚合的。比如如果我們在1分鐘內上報了2次,一次10,一次20,那麼1分鐘的聚合就是總數30,每次上報記錄的是聚合的總和信息。

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

3、執行時間信息,可以方便的統計方法執行時間。上報的信息會比數量信息豐富,除了次數之外還有總執行時間、平均執行時間、最長執行時間。

朱曄和你聊Spring系列S1E7:簡單好用的Spring Boot Actuator

是不是很方便,使用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 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系列S1E7:簡單好用的Spring Boot Actuator

總結

本文我們通過一些例子覆蓋瞭如下內容:

  1. Actuator模塊的功能。
  2. 詳述了健康檢查端點,自定義健康檢查項。
  3. 詳述了數據信息端點,為程序添加各種附加數據項。
  4. 詳述了監控打點,程序方式上報自定義的三種形式的打點。
  5. InfluxDb和Grafana的簡單使用。

的確,每一個功能都是簡單的幾步配置,Spring Boot Actuator真的很方便,這些功能是一個真正的可用於生產環境的程序必不可少的一些功能,Spring Boot不僅僅為我們提供了方便,而且為我們定義了架構模板,讓每一個開發人員都能有意識,應該做一些什麼,這也就是我為什麼一直說Spring引領了企業級單機開發,Spring Boot引領了互聯網微服務開發。

但是,Spring Boot因為在高速發展,會不斷吸收好的開源項目整合到生態中去,所以在API上變化會較多,API一直在修改,增加了不少學習成本和坑。任何事情都有兩面性,我們在要求Spring生態為我們提供越來越多功能的時候,享受到便利的同時,也必須去適應Spring的快速變化。


分享到:


相關文章: