本文首發於個人網站:Spring Boot應用的健康監控
在之前的系列文章中我們學習了如何進行Spring Boot應用的功能開發,以及如何寫單元測試、集成測試等,然而,在實際的軟件開發中需要做的不僅如此:還包括對應用程序的監控和管理。
正如飛行員不喜歡盲目飛行,程序員也需要實時看到自己的應用目前的運行情況。如果給定一個具體的時間,我們希望知道此時CPU的利用率、內存的利用率、數據庫連接是否正常以及在給定時間段內有多少客戶請求等指標;不僅如此,我們希望通過圖表、控制面板來展示上述信息。最重要的是:老板和業務人員希望看到的是圖表,這些比較直觀易懂。
首先,這篇文章講介紹如何定制自己的health indicator。
實戰
- 在pom文件中添加spring-boot-starter-actuator依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- spring-boot-starter-actuator這個庫讓我們可以訪問應用的很多信息,包括:/env、/info、/metrics、/health等。現在運行程序,然后在瀏覽器中訪問:http://localhost:8080/health,將可以看到下列內容。
- 除了/health可以訪問,其他的Endpoints也可以訪問,例如/info:首先在application.properties文件中添加對應的屬性值,符號@包圍的屬性值來自pom.xml文件中的元素節點。
info.build.artifact=@project.artifactId@
info.build.name=@project.name@
info.build.description=@project.description@
info.build.version=@project.version@
- 要獲取配置文件中的節點值,需要在pom文件中進行一定的配置,首先在
節點里面添加:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
然后在
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<delimiters>
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
- 然后運行應用程序,訪問http://localhost:8080/info,可以看到下列信息
- 除了使用系統默認的監控信息,我們還可以定義自己的health indicator。使用Spring Boot:定制自己的starter一文中做過的db-count-starter作為觀察對象,我們希望監控每個數據庫接口的運行狀況:如果某個接口返回的個數大於等於0,則表示系統正常,表示為UP狀態;否則,可能該接口發生異常,表示為DOWN狀態。首先,將DbCountRunner類中的getRepositoryName方法由private轉為protected,然后在db-count-starter這個模塊中也添加actuator依賴。
- 在db-count-starter/src/main/com/test/bookpubstarter目錄下創建DbCountHealthIndicator.java文件
public class DbCountHealthIndicator implements HealthIndicator {
private CrudRepository crudRepository;
public DbCountHealthIndicator(CrudRepository crudRepository) {
this.crudRepository = crudRepository;
}
@Override
public Health health() {
try {
long count = crudRepository.count();
if (count >= 0) {
return Health.up().withDetail("count", count).build();
} else {
return Health.unknown().withDetail("count", count).build();
}
} catch (Exception e) {
return Health.down(e).build();
}
}
}
- 最后,還需要注冊剛剛創建的健康監控器,在DbCountAutoConfiguration.java中增加如下定義:
@Autowired
private HealthAggregator healthAggregator;
@Bean
public HealthIndicator dbCountHealthIndicator(Collection<CrudRepository> repositories) {
CompositeHealthIndicator compositeHealthIndicator = new
CompositeHealthIndicator(healthAggregator);
for (CrudRepository repository: repositories) {
String name = DbCountRunner.getRepositoryName(repository.getClass());
compositeHealthIndicator.addHealthIndicator(name, new DbCountHealthIndicator(repository));
}
return compositeHealthIndicator;
}
- 運行程序,然后訪問http://localhost:8080/health,則可以看到如下結果
分析
Spring Boot Autuator這個庫包括很多自動配置,對外開放了很多endpoints,通過這些endpoints可以訪問應用的運行時狀態:
- /env提供應用程序的環境變量,如果你在調試時想知道某個配置項在運行時的值,可以通過這個endpoint訪問——訪問http://localhost:8080/env,可以看到很多方面的配置,例如,class path resources—[tomcat.https.properties]、applicationConfig—[classpath:/application.properties]、commonsConfig、systemEnvironment、systemProperties等。
這些變量的值由Environment實例中的PropertySource實例保存,根據這些屬性值所在的層次,有可能在運行時已經做了值替換,跟配置文件中的不一樣了。為了確認某個屬性的具體值,例如book.count.rate屬性,可以訪問http://localhost:8080/env/book.counter.rate來查詢,如果跟配置文件中的不一樣,則可能是被系統變量或者命令行參數覆蓋了。EnvironmentEndpoint類負責實現上述功能,有興趣可以再看看它的源碼; - /configprops提供不同配置對象,例如WebConfiguration.TomcatSslConnectionProperties,它與/env不同的地方在於它會表示出與配置項綁定的對象。嘗試下訪問http://localhost:8080/configprops,然后在網頁中查詢custom.tomcat.https,可以看到我們之前用於配置TomcatSslConnector對象的屬性值(參見:讓你的Spring Boot工程支持HTTP和HTTPS)。
- /autoconfig以web形式對外暴露AutoConfiguration 信息,這些信息的解釋可以參考Spring Boot:定制自己的starter一文,這樣我們就不需要通過“修改應用程序的日志級別和查看應用的啟動信息”來查看應用的自動配置情況了。
- /beans,這個endpoint列出所有由Spring Boot創建的bean。
- /mapping,這個endpoint顯示當前應用支持的URL映射,該映射關系由HandlerMapping類維護,通過這個endpoint可以查詢某個URL的路由信息。
- /info,這個endpoint顯示應用程序的基本描述,在之前的實踐例子中我們看過它的返回信息,屬性值來自appliaction.properties,同時也可以使用占位符獲取pom.xml文件中的信息。任何以info.開頭的屬性都會在訪問http://localhost:8080/info時顯示。
- /health提供應用程序的健康狀態,或者是某個核心模塊的健康狀態。
- /metrics,這個endpoint顯示Metrics 子系統管理的信息,后面的文章會詳細介紹它。
上述各個endpoint是Spring Boot Actuator提供的接口和方法,接下來看看我們自己定制的HealthIndicator,我們只需要實現HealthIndicator接口,Spring Boot會收集該接口的實現,並加入到/health這個endpoint中。
在我們的例子中,我們為每個CrudRepository實例都創建了一個HealthIndicator實例,為此我們創建了一個CompositeHealthIndicator實例,由這個實例管理所有的DbHealthIndicator實例。作為一個composite,它會提供一個內部的層次關系,從而可以返回JSON格式的數據。
代碼中的HealthAggregator實例的作用是:它維護一個map,告訴CompositeHealthIndicator如何決定所有HealthIndicator代表的整體的狀態。例如,除了一個repository返回DOWN其他的都返回UP,這時候這個composite indicator作為一個整體應該返回UP還是DOWN,HealthAggregator實例的作用就在這里。
Spring Boot使用的默認的HealthAggregator實現是OrderedHealthAggregator,它的策略是手機所有的內部狀態,然后選出在DOWN、OUT_OF_SERVICE、UP和UNKNOWN中間具有最低優先級的那個狀態。這里使用策略設計模式,因此具體的狀態判定策略可以改變和定制,例如我們可以創建定制的HealthAggregator:
最后需要考慮下安全問題,通過這些endpoints暴露出很多應用的信息,當然,Spring Boot也提供了配置項,可以關閉指定的endpoint——在application.properties中配置
還可以通過設置management.port=-1關閉endpoint的HTTP訪問接口,或者是設置其他的端口,供內部的admin服務訪問;除了控制端口,還可以設置僅僅讓本地訪問,只需要設置management.address=127.0.0.1;通過設置management.context-path=/admin,可以設置指定的根路徑。綜合下,經過上述設置,在本地訪問http://127.0.0.1/admin/health來訪問健康狀態。
可以在防火牆上屏蔽掉不是/admin/*的endpoints訪問請求,更進一步,利用Spring Security可以配置驗證信息,這樣要訪問當前應用的endpoints必須使用用戶名和密碼登陸。
參考資料
Spring Boot 1.x系列
- Spring Boot的自動配置、Command-line-Runner
- 了解Spring Boot的自動配置
- Spring Boot的@PropertySource注解在整合Redis中的使用
- Spring Boot項目中如何定制HTTP消息轉換器
- Spring Boot整合Mongodb提供Restful接口
- Spring中bean的scope
- Spring Boot項目中使用事件派發器模式
- Spring Boot提供RESTful接口時的錯誤處理實踐
- Spring Boot實戰之定制自己的starter
- Spring Boot項目如何同時支持HTTP和HTTPS協議
- 自定義的Spring Boot starter如何設置自動配置注解
- Spring Boot項目中使用Mockito
- 在Spring Boot項目中使用Spock測試框架
- Spring Boot項目中如何定制攔截器
- Spring Boot項目中如何定制PropertyEditors
- Spring Boot構建的Web項目如何在服務端校驗表單輸入
本號專注於后端技術、JVM問題排查和優化、Java面試題、個人成長和自我管理等主題,為讀者提供一線開發者的工作和成長經驗,期待你能在這里有所收獲。