spring-boot-starter-actuator與應用監控

所有的應用開發完成之後,其最終目的都是為了上線運行,SpringBoot 應用也不例外,而在應用運行的漫長生命週期內,為了保障其可以持續穩定的服務,我們通常需要對其進行監控,從而可以瞭解應用的運行狀態,並根據情況決定是否需要對其運行狀態進行調整。


順應需求,SpringBoot 框架提供了 spring-boot-starter-actuator 自動配置模塊用於支持 SpringBoot 應用的監控。Actuator 這個詞即使翻譯過來也不是很容易理解(比如翻譯成“制動器;傳動裝置;執行機構”等)。
如圖 1 所示,形象的描述了 Actuator 是什麼。

spring-boot-starter-actuator與應用監控

圖 1 Sensor 和 Actuator 示意圖


為了能夠感知應用的運行狀態,我們通常會設置一些監控指標並採集分析,這些監控指標的採集需要在應用內部設置相應的監控點,這類監控點一般只是讀取狀態數據,我們通常稱它們為 Sensor,即中文一般稱為“傳感器”的東西。
應用的運行狀態數據通過 Sensors 採集上來之後,我們通常會有專門的系統對這些數據進行分析和判斷,一旦某個指標數據超出了預定的閾值,這往往意味著應用的運行狀態在這個指標上出現了“不健康”的現象,我們希望對這個指標進行調整,而為了能夠執行調整,我們需要預先在應用內部設置對應的執行調整邏輯的控制器。
比如,直接關閉的開關,或者可以執行微調甚至像剎車一樣直接快速拉低某個指標值的裝置,這些控制器就稱為 Actuator。雖然我們日常天天在說“監控,監控”,但實際上“監”跟“控”是兩個概念,Sensor 更多服務於“監”的場景,而 Actuator 則服務於“控”的場景。
spring-boot-starter-actuator 自動配置模塊默認提供了很多 endpoint,雖然自動配置模塊名為 spring-boot-starter-actuator,但實際上這些 endpoint 可以按照“監”和“控”劃分為兩類:

1. Sensor 類 endpoints

名稱說明autoconfig這個 endpoint 會為我們提供一份 SpringBoot 的自動配置報告,告訴我們哪些自動配置模塊生效了,以及哪些沒有生效,原因是什麼。beans給出當前應用的容器中所有 bean 的信息。configprops對現有容器中的 ConfigurationProperties 提供的信息進行“消毒”處理後給出彙總信息。info提供當前 SpringBoot 應用的任意信息,我們可以通過 Environment 或者 application.properties 等形式提供以 info. 為前綴的任何配置項,然後 info 這個 endpoint 就會將這些配置項的值作為信息的一部分展示出來。health針對當前 SpringBoot 應用的健康檢查用的 endpoint。env關於當前 SpringBoot 應用對應的 Environment 信息。metrics當前 SprinBoot 應用的 metrics 信息。trace當前 SpringBoot 應用的 trace 信息。mapping如果是基於 SpringMVC 的 Web 應用,mapping 這個 endpoint 將給出 @RequestMapping 相關信息。

2. Actuator 類 endpoints

  • shutdown:用於關閉當前 SpringBoot 應用的 endpoint。
  • dump:用於執行線程的 dump 操作。


默認情況下,除了 shutdown 這個 endpoint(因為比較危險,如果沒有安全防護,誰都可以訪問它,然後關閉應用),其他 endpoints 都是默認啟用的。
生產環境下,如果沒有啟用安全防護(比如沒有依賴 spring-boot-starter-security),那麼,建議遵循 Deny By Default 原則,將所有的 endpoints 都關掉,然後根據具體情況單獨啟用某些 endpoint:

endpoints.enabled=falseendpoints.info.enabled=trueendpoints.health.enabled=true...

所有配置項以 endpoints. 為前綴,然後根據 endpoint 名稱劃分具體配置項。大部分 endpoints 都是開箱即用,但依然有些 endpoint 提供給我們進一步擴展的權利,比如健康狀態檢查相關的 endpoint(health endpoint)。

自定義應用的健康狀態檢查

應用的健康狀態檢查是很普遍的監控需求,SpringBoot 也預先通過 org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration 為我們提供了一些常見服務的監控檢查支持,比如:

  • DataSourceHealthIndicator
  • DiskSpaceHealthIndicator
  • RedisHealthIndicator
  • SolrHealthIndicator
  • MongoHealthIndicator


如果這些默認提供的健康檢查支持依然無法滿足我們的需要,SpringBoot 還允許我們提供更多的 HealthIndicator 實現,只要將這些 HealthIndicator 實現類註冊到 IoC 容器,SpringBoot 會自動發現並使用它們。
假設需要檢查依賴的 dubbo 服務是否處於健康狀態,我們可以實現一個 DubboHealthIndicator:

<code>import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.rpc.service.EchoService;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;

import org.springframework.boot.actuate.health.Health;
public class DubboHealthIndicator extends AbstractHealthIndicator {
private final ReferenceBean bean;
public DubboHealthIndicator(ReferenceBean bean) {
this.bean = bean;
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
builder.withDetail("interface", bean.getObjectType());
final EchoService service = (EchoService) bean.getObject();
service.$echo("hi");
builder.up();
}
}/<code>

要實現一個自定義的 HealthIndicator,一般我們不會直接實現(Implements)HealthIndicator 接口,而是繼承 AbstractHealthIndicator:

<code>public abstract class AbstractHealthIndicator implements HealthIndicator {
@Override
public final Health health() {
Health.Builder builder = new Health.Builder();
try {
doHealthCheck(builder);
} catch (Exception ex) {
builder.down(ex);
}
return builder.build();
}
protected abstract void doHealthCheck(Health.Builder builder)
throws Exception;
}/<code>

好處就是,我們只需實現 doHealthCheck,在其中實現我們面向的具體服務的健康檢查邏輯就可以了,因此,在 DubboHealthIndicator 實現類中,我們通過 dubbo 框架提供的 EchoService 直接檢查相應的 dubbo 服務健康狀態即可,只要沒有任何異常拋出,我們就認為檢查的 dubbo 服務是狀態健康的,所以,最後會通過 Health.Builder 的 up() 方法標記服務狀態為正常運行。
為了完成對 dubbo 服務的健康檢查,只實現一個 DubboHealthIndicator 是不夠的,我們還需要將其註冊到 IoC 容器中,但是一個一個單獨註冊太費勁了,而且還要自己提供針對某個 dubbo 服務的 ReferenceBean 依賴實例。


所以,為了一勞永逸,也為了其他人能夠同樣方便地使用針對 dubbo 服務的健康檢查支持,我們可以在 DubboHealthIndicator 的基礎上實現一個 spring-boot-starter-dubbo-health-indicator 自動配置模塊,即:

<code>@Configuration
@ConditionalOnClass(name = { "com.alibaba.dubbo.rpc.Exporter" })
public class DubboHealthIndicatorConfiguration {
@Autowired
HealthAggregator healthAggregator;
@Autowired(required = false)
Map<string> references;
@Bean
public HealthIndicator dubboHealthIndicator() {
Map<string> indicators = new HashMap<>();
for (String key : references.keySet()) {
final ReferenceBean bean = references.get(key);
indicators.put(key.startsWith("&") ? key.replaceFirst("&", "")
: key, new DubboHealthIndicator(bean));
}
return new CompositeHealthIndicator(healthAggregator, indicators);
}
}/<string>/<string>/<code>

然後我們在 spring-boot-starter-dubbo-health-indicator 的 META-INF/spring.factories 文件中添加如下配置:

<code>org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\com.keevol...DubboHealthIndicatorConfiguration /<code>

現在,發佈 spring-boot-starter-dubbo-health-indicator 並依賴它就可以自動享受到針對當前應用引用的所有 dubbo 服務進行健康檢查的服務。
那麼針對 Map<string>references 的依賴注入是從哪裡來的?
其實 Spring 框架支持依賴注入 Key 的類型為 String 的 Map,遇到這種類型的 Map 聲明(Map),Spring 框架將掃描容器中所有類型為 T 的 bean,然後以該 bean 的 name 作為 Map 的 Key,以 bean 實例作為對應的 Value,從而構建一個 Map 並注入到依賴處。
/<string>

開放的 endpoints 才真正“有用”

不管是 spring-boot-starter-actuator 默認提供的 endpoint 實現,還是我們自己給出的 endpoint 實現,如果只是實現了放在 SpringBoot 應用的“身體內部”,那麼它們不會發揮任何作用,只有將它們採集的信息暴露開放給外部監控者,或者允許外部監控者訪問它們,這些 endpoints 才會真正發揮出它們的最大“功效”。
首先,spring-boot-starter-actuator 會通過 org.springframework.boot.actuate.autoconfigure.EndpointMBeanExportAutoConfiguration 將所有的 org.springframework.boot.actuate.endpoint.Endpoint 實例以 JMX MBean 的形式開放給外部監控者使用。
默認情況下,這些 Endpoint 對應的 JMX MBean 會放在 org.springframework.boot 命名空間下面,不過可以通過 endpoints.jmx.domain 配置項進行更改,比如 endpoints.jmx.domain=com.keevol.management。
EndpointMBeanExportAutoConfiguration 為我們提供了一條很好的應用監控實踐之路,既然它會把所有的 org.springframework.boot.actuate.endpoint.Endpoint 實例都作為 JMX Mbean 開放出去,那麼,我們就可以提供一批用於某些場景下的自定義 Endpoint 實現類,比如:

<code>public class HelloEndpoint extends AbstractEndpoint<string> {
public HelloEndpoint(String id) {
super(id, false);
}
@Override
public String invoke() {
return "Hello, SpringBoot";
}
}/<string>/<code>


然後,將像 HelloEndpoint 這樣的實現類註冊到 SpringBoot 應用的 IoC 容器,就可以擴展 SpringBoot 的 endpoints 功能了。
Endpoint 其實更適合簡單的 Sensor 場景(即用於讀取或者提供信息),或者簡單功能的 actuator 場景(不需要行為參數),如果需要對 SpringBoot 進行更細粒度的監控,可以考慮直接使用 Spring 框架的 JMX 支持。


除了可以使用 JMX 將 spring-boot-starter-actuator 提供的(或者我們自己提供的)endpoints 開放訪問,如果當前 SpringBoot 應用恰好又是一個 Web 應用。那麼,這些 endpoints 還會通過 HTTP 協議開放給外部訪問,與一般的 Web 請求處理一樣,使用的也是 Web 應用使用的 HTTP 服務器和地址端口。
因為每個 Endpoint 都有一個 id 作為唯一標識,所以,這些 endpoints 的默認訪問路徑其實就是它們的 id,比如 info 這個 endpoint 的 HTTP 訪問路徑就是 /info,而 beans 這個 endpoint 的 HTTP 訪問路徑則是 /beans,以此類推。
SpringBoot 允許我們通過 management. 為前綴的配置項對 endpoints 的 HTTP 開放行為進行調整:

  • 使用 management.context-path=設置自定義的 endpoints 訪問上下文路徑,默認直接根路徑,即 /info,/beans 等形式。
  • 使用 management.address= 配置單獨的 HTTP 服務監聽地址,比如只允許本地訪問。


management.address=127.0.0.1 使用 management.port=設置單獨的監聽端口,默認與 web 應用的對外服務端口相同。
我們可以通過 management.port=8888 將管理接口的 HTTP 對外監聽端口設置為 8888,但如果 management.port=-1,則意味著我們將關閉管理接口的 HTTP 對外服務。
JMX 和 HTTP 是 endpoints 對外開放訪問最常用的方式,鑑於 Java 的序列化漏洞以及 JMX 的遠程訪問防火牆問題,建議用 HTTP 並配合安全防護將 SpringBoot 應用的 endpoints 開放給外部監控者使用。

用還是不用,這是個問題

endpoints 屬於 spring-boot-starter-actuator 提供的主要功能之一,除此之外,spring-boot-starter-actuator 還提供了更多針對應用監控的支持和實現方案。

1. CrshAutoConfiguration 與 spring-boot-starter-remote-shell

spring-boot-starter-actuator 提供了基於 CRaSH(http://www.crashub.org/)的遠程 Shell(Remote Shell)支持,從筆者角度來看,這是一把雙刃劍,不建議在生產環境使用,因為提供給自己便利的同時,也為黑客朋友們提供了便利。如果實在要用,請加強安全認證和防護。
不過,這裡我們還是會為大家分析一下 spring-boot-starter-actuator 是如何提供針對 CRaSH 的支持的。
spring-boot-starter-actuator 提供了 org.springframework.boot.actuate.autoconfigure.CrshAutoConfiguration 自動配置類,該類會在 org.crsh.plugin.PluginLifeCycle 出現在 classpath 中的時候生效。
所以,只要將 CRaSH 作為依賴加入應用的 classpath 依賴就可以了,最簡單直接的做法是讓需要啟用 CRaSH 的 SpringBoot 應用依賴 spring-boot-starter-remote-shell 自動配置模塊,spring-boot-starter-remote-shell 的主要功效就是提供了針對 CRaSH 的各項依賴。

2. SpringBoot 的 Metrics 與 Dropwizard 的 Metrics

SpringBoot 提供了一套自己的針對系統指標的度量框架,這個框架的核心設計如圖 2 所示。

spring-boot-starter-actuator與應用監控

圖 2 SpringBoot 框架的 Metrics 核心類設計示意圖


基本上,我們只需關注 org.springframework.boot.actuate.endpoint.PublicMetrics 即可,它可以理解為提供一組 Metric 的集合,我們既可以通過 PublicMetrics 來彙總和管理 Metric,也可以通過 MetricRepository 來存儲和管理 Metric。
一旦使用了 spring-boot-starter-actuator,只要當前 SpringBoot 應用的 ApplicationContext 中存在任何 PublicMetrics 實例,EndpointAutoConfiguration 就會將這些 PublicMetrics 採集彙總到一起,然後通過 MetricsEndpoint 將它們開放出去。
spring-boot-starter-actuator 提供的 org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration 默認會把一個 SystemPublicMetrics 開放出來,用於提供系統各項指標的度量和狀態採集,另外一個就是會把當前 SpringBoot 應用的 ApplicationContext 的 org.springframework.boot.actuate.metrics.repository.MetricRepository 實例中的所有 Metric 彙總並開放出去。
默認如果用戶沒有給出任何自定義的 MetricRepository,spring-boot-starter-actuator 會提供一個 InMemoryMetricRepository 實現,如果我們將 Dropwizard 的 Metrics 類庫作為依賴加入 classpath,那麼,Dropwizard Metrics 的 MetricRegistry 中所有的度量指標項也會通過 PublicMetrics 的形式開發暴露出來。
雖然 SpringBoot 提供的 metrics 框架也能幫助我們完成系統和應用指標的度量,但筆者更傾向於使用 Dropwizard 這種特定場景下比較完善的方案,從 metrics 的類型,到外圍系統的集成,Dropwizard metrics 都更加成熟和完備。

3. Auditing 與 Trace

SpringBoot 的 Auditing 和 Trace 支持都遵循數據/事件+Repository 的設計(如圖 3 所示)。

spring-boot-starter-actuator與應用監控

圖 3 SpringBoot 框架 Audit 和 Trace 功能支持核心類示意圖


從設計上來說是很簡單清晰的,也有很好的統一性,但實際應用過程中,我們依然會更加傾向於特定場景的方案選型,比如 Auditing。
我們可能只是通過打印日誌時候的 Logger 名稱來區分並記錄 Audit 事件,然後通過日誌採集通道彙總分析就可以了,而不用非要實現一個 LogFileBasedAuditEventRepository 或者 ElasticSearchBasedAuditEventRepository 之類的實現,否則看起來難免有些“學究”氣。對於 Trace 來說也是同樣道理,我們可能直接使用完備的 APM 方案而不是單一或者少量 Trace 事件的記錄。

最後

有需要Spring Boot視頻教程的小夥伴們注意啦:

點贊+關注+轉發+私信關鍵詞【boot】即可免費領取!


分享到:


相關文章: