Spring Boot中的健康指標

1.概述

Spring Boot提供了幾種不同的方法來檢查正在運行的應用程序及其組件的狀態和運行狀況。在這些方法中, HealthContributorHealthIndicator API是其中兩個。

在本教程中,我們將熟悉這些API,了解它們的工作原理,並了解如何為它們提供定制信息。

2.依賴關係

健康信息貢獻者是Spring Boot執行器模塊的一部分,因此我們需要適當的Maven依賴項

<dependency>

 <groupId>org.springframework.boot</groupId>

 <artifactId>spring-boot-starter-actuator</artifactId>

 </dependency>

3.內置的HealthIndicator

Spring Boot開箱即用地註冊了許多HealthIndicator以報告特定應用程序方面的運行狀況

這些指標中的一些幾乎總是被註冊的,例如DiskSpaceHealthIndicatorPingHealthIndicator 。前者報告磁盤的當前狀態,後者充當應用程序的ping端點。

另一方面,Spring Boot有條件地註冊一些指示器。也就是說,如果某些依賴關係在類路徑上,或者滿足其他一些條件,Spring Boot可能還會註冊其他一些HealthIndicator 。例如,如果我們使用關係數據庫,那麼Spring Boot註冊DataSourceHealthIndicator 。同樣,如果我們碰巧將Cassandra用作數據存儲,它將註冊CassandraHealthIndicator

為了檢查Spring Boot應用程序的運行狀況,我們可以調用/actuator/health端點。該端點將報告所有已註冊HealthIndicator的匯總結果。

另外,要從一個特定指標查看運行狀況報告,我們可以調用/actuator/health/{name}端點。例如,調用/actuator/health/diskSpace DiskSpaceHealthIndicator返回狀態報告:

{

 "status": "UP",

 "details": {

 "total": 499963170816,

 "free": 134414831616,

 "threshold": 10485760,

 "exists": true

 }

 }

4.自定義HealthIndicator

除了內置的健康指標外,我們還可以註冊自定義HealthIndicator以報告組件或子系統的健康狀況。為此,我們要做的就是將HealthIndicator接口的實現註冊為Spring bean

例如,以下實現隨機報告失敗:

@Component

 public class RandomHealthIndicator implements HealthIndicator {



 @Override

 public Health health() {

 double chance = ThreadLocalRandom.current().nextDouble();

 Health.Builder status = Health.up();

 if (chance > 0.9) {

 status = Health.down();

 }

 return status.build();

 }

 }

根據此指標的運行狀況報告,應僅在90%的時間內啟動應用程序。這裡,我們使用Health建設者上報的健康信息。

**但是,在反應式應用程序中,我們應該註冊一個ReactiveHealthIndicator**類型的Bean。反應性health()方法返回Mono<Health>而不是簡單的Health 。除此之外,這兩種Web應用程序類型的其他詳細信息都相同。

4.1 指標名稱

要查看有關該特定指標的報告,我們可以調用/actuator/health/random端點。例如,下面是API響應的樣子:

{"status": "UP"}

random/actuator/health/random URL是該指示器的標識。 HealthIndicator實現的標識符**等於不帶HealthIndicator**後綴的Bean名稱。由於bean名稱是randomHealthIdenticator ,所以random前綴將是標識符。

使用此算法,如果我們將bean名稱更改為rand

@Component("rand")

 public class RandomHealthIndicator implements HealthIndicator {

 // omitted

 }

然後,指標標識符將是rand而不是random

4.2 禁用指標

要禁用特定指示器,我們可以將 management.health.<indicator_identifier>.enabled”配置屬性設置為false 。例如,如果我們將以下內容添加到application.properties

management.health.random.enabled=false

然後,Spring Boot將禁用RandomHealthIndicator 。要激活此配置屬性,我們還應該在指標上@ConditionalOnEnabledHealthIndicator

@Component

 @ConditionalOnEnabledHealthIndicator("random")

 public class RandomHealthIndicator implements HealthIndicator {

 // omitted

 }

現在,如果我們調用/actuator/health/random ,Spring Boot將返回404 Not Found HTTP響應:

@SpringBootTest

 @AutoConfigureMockMvc

 @TestPropertySource(properties = "management.health.random.enabled=false")

 class DisabledRandomHealthIndicatorIntegrationTest {



 @Autowired

 private MockMvc mockMvc;



 @Test

 void givenADisabledIndicator_whenSendingRequest_thenReturns404() throws Exception {

 mockMvc.perform(get("/actuator/health/random"))

 .andExpect(status().isNotFound());

 }

 }

請注意,禁用內置或自定義指標彼此相似。因此,我們也可以將相同的配置應用於內置指示器。

4.3 額外細節

除了報告狀態外,我們還可以使用withDetail(key, value)附加其他鍵值詳細信息:

public Health health() {

 double chance = ThreadLocalRandom.current().nextDouble();

 Health.Builder status = Health.up();

 if (chance > 0.9) {

 status = Health.down();

 }



 return status

 .withDetail("chance", chance)

 .withDetail("strategy", "thread-local")

 .build();

 }

在這裡,我們向狀態報告中添加了兩條信息。另外,我們可以通過將Map<String, Object>傳遞給withDetails(map)方法來實現相同的目的:

Map<String, Object> details = new HashMap<>();

 details.put("chance", chance);

 details.put("strategy", "thread-local");



 return status.withDetails(details).build();

現在,如果我們調用/actuator/health/random ,我們可能會看到類似以下內容:

{

 "status": "DOWN",

 "details": {

 "chance": 0.9883560157173152,

 "strategy": "thread-local"

 }

 }

我們也可以通過自動測試來驗證此行為:

mockMvc.perform(get("/actuator/health/random"))

 .andExpect(jsonPath("$.status").exists())

 .andExpect(jsonPath("$.details.strategy").value("thread-local"))

 .andExpect(jsonPath("$.details.chance").exists());

與系統組件(例如數據庫或磁盤)通信時,有時會發生異常。我們可以使用withException(ex)方法報告此類異常:

if (chance > 0.9) {

 status.withException(new RuntimeException("Bad luck"));

 }

我們還可以將異常傳遞給前面看到down(ex)

if (chance > 0.9) {

 status = Health.down(new RuntimeException("Bad Luck"));

 }

現在,運行狀況報告將包含堆棧跟踪:

{

 "status": "DOWN",

 "details": {

 "error": "java.lang.RuntimeException: Bad Luck",

 "chance": 0.9603739107139401,

 "strategy": "thread-local"

 }

 }

4.4 細節曝光

management.endpoint.health.show-details配置屬性控制每個運行狀況終結點可以公開的詳細信息級別。

例如,如果我們將此屬性設置為always,那麼Spring Boot將總是返回details字段,就像上面的示例一樣。

另一方面,如果我們將此屬性設置為never ,那麼Spring Boot將始終忽略output中details 。還有一個when_authorized值,僅向授權用戶details用戶只有在以下情況下才能獲得授權:

  • 她已通過身份驗證
  • 她擁有在management.endpoint.health.roles配置屬性中指定的角色

4.5 健康狀況

默認情況下,Spring Boot定義四個不同的值作為運行Status`:

  • UP —組件或子系統按預期工作
  • DOWN —組件不起作用
  • OUT_OF_SERVICE —組件暫時停止服務
  • UNKNOWN —組件狀態未知

這些狀態被聲明為[public static final](https://github.com/spring-projects/spring-boot/blob/310ef6e9995fab302f6af8b284d0a59ca0f212e9/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/Status.java#L43)實例,而不是Java枚舉。因此可以定義我們自己的自定義健康狀態。為此,我們可以使用[status(name)](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/actuate/health/Health.Builder.html#status-java.lang.String-)方法:

Health.Builder warning = Health.status("WARNING");

健康狀況會影響健康終結點的HTTP狀態代碼。默認情況下,Spring Boot映射DOWNOUT_OF_SERVICE狀態以拋出503狀態代碼。另一方面, UP和任何其他未映射狀態將轉換為200 OK狀態代碼。

要自定義此映射,我們可以將management.endpoint.health.status.http-mapping.<status>配置屬性設置為所需的HTTP狀態代碼號:

management.endpoint.health.status.http-mapping.down=500

 management.endpoint.health.status.http-mapping.out_of_service=503

 management.endpoint.health.status.http-mapping.warning=500

現在Spring Boot將DOWN狀態映射到500,將OUT_OF_SERVICE到503,將WARNING映射到500 HTTP狀態代碼:

mockMvc.perform(get("/actuator/health/warning"))

 .andExpect(jsonPath("$.status").value("WARNING"))

 .andExpect(status().isInternalServerError());

同樣, [HttpCodeStatusMapper](https://docs.spring.io/spring-boot/docs/2.2.3.RELEASE/api/org/springframework/boot/actuate/health/HttpCodeStatusMapper.html)類型的bean來自定義HTTP狀態代碼映射

@Component

 public class CustomStatusCodeMapper implements HttpCodeStatusMapper {



 @Override

 public int getStatusCode(Status status) {

 if (status == Status.DOWN) {

 return 500;

 }



 if (status == Status.OUT_OF_SERVICE) {

 return 503;

 }



 if (status == Status.UNKNOWN) {

 return 500;

 }



 return 200;

 }

 }

getStatusCode(status)方法將運行狀況作為輸入,並返回HTTP狀態代碼作為輸出。此外,還可以映射自定義的Status實例:

if (status.getCode().equals("WARNING")) {

 return 500;

 }

默認情況下,Spring Boot使用默認映射註冊此接口的簡單實現。 [SimpleHttpCodeStatusMapper](https://docs.spring.io/spring-boot/docs/2.2.3.RELEASE/api/org/springframework/boot/actuate/health/SimpleHttpCodeStatusMapper.html)還能夠從配置文件中讀取映射。

5.健康信息與指標

非平凡的應用程序通常包含一些不同的組件。例如,考慮使用Cassandra作為數據庫,使用Apache Kafka作為其發布-訂閱平台,使用Hazelcast作為其內存數據網格的Spring Boot應用程序。

我們應該使用HealthIndicator來查看應用程序是否可以與這些組件進行通信。如果通信鏈接失敗,或者組件本身發生故障或運行緩慢,則我們應了解組件狀態不正常。換句話說,這些指標應用於報告不同組件或子系統的健康狀況。

相反,我們應該避免使用HealthIndicator來測量值,對事件進行計數或測量持續時間。這就是為什麼我們有指標。簡而言之,指標是報告CPU使用率,平均負載,堆大小,HTTP響應分佈等的更好工具。

六,結論

在本教程中,我們看到瞭如何向執行器運行狀況端點貢獻更多的運行狀況信息。此外,我們深入介紹了健康API中的不同組件,例如HealthStatus和HTTP狀態映射的狀態。

最後,我們就健康信息和指標之間的區別進行了快速討論,並了解了何時使用它們。

像往常一樣,所有示例都可以 在GitHub上找到