微服務的特點決定了功能子產品的部署是分布式的,⼤部分功能子產品都是運⾏在不同的機器上,彼此通過服務 調⽤進⾏互動,前背景的業務流會經過很多個微服務的處理和傳遞,出現異常如何快速定位便成為了⼀個問 題,在這種架構下微服務的監控顯得尤為重要。 Spring Boot 是⼀個⾃帶監控的開源架構,元件 Spring Boot Actuator 負責監控應⽤的各項靜态和動态的變 量。項⽬結合 Spring Boot Actuator 的使⽤,便可輕松對 Spring Boot 應⽤監控治理, Spring Boot 的 Actuator 提供了很多⽣産級的特性,⽐如監控和度量 Spring Boot 應⽤程式,這些特性可以通過衆多 REST 接⼝、遠端 Shell 和 JMX 獲得。
Actuator 監控
Spring Boot 使⽤ “ 習慣優于配置的理念 ” ,采⽤包掃描和⾃動化配置的機制來加載依賴 jar 中的 Spring Bean , 不需要任何 XML 配置,就可以實作 Spring 的所有配置。雖然這樣做能讓代碼變得⾮常簡潔,但是整個應⽤ 的執行個體建立和依賴關系等資訊都被離散到了各個配置類的注解上,這使得我們分析整個應⽤中資源和執行個體的 各種關系變得⾮常的困難。 隻需要在項⽬中添加 spring-boot-starter-actuator ,就⾃動啟⽤了監控功能。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
添加 spring-boot-starter-web 主要是為了保持應⽤運⾏狀态。 Spring Boot Actuator 是 Spring Boot 提供的對應⽤系統的檢查和監控的內建功能,可以檢視應⽤配置的詳細 資訊,例如⾃動化配置資訊、建立的 Spring beans 以及⼀些環境屬性等。
Actuator 的 REST 接⼝
Actuator 監控分成兩類:原⽣端點和⽤戶⾃定義端點。⾃定義端點主要是指擴充性,⽤戶可以根據⾃⼰的實 際應⽤,定義⼀些⽐較關⼼的名額,在運⾏期進⾏監控。 原⽣端點是在應⽤程式⾥提供衆多 Web 接⼝,通過它們了解應⽤程式運⾏時的内部狀況,原⽣端點⼜可以分 成三類:
- 應⽤配置類,可以檢視應⽤在運⾏期的靜态資訊,例如⾃動配置資訊、加載的 springbean 資訊、yml ⽂ 件配置資訊、環境資訊、請求映射資訊;
- 度量名額類,主要是運⾏期的動态資訊,如堆棧、請求連、⼀些健康名額、metrics 資訊等;
- 操作控制類,主要是指 shutdown,⽤戶可以發送⼀個請求将應⽤的監控功能關閉。
Actuator 提供了 13 個接⼝,具體如下表所示。
HTTP ⽅方法 | 路路徑 | 描述 |
GET | /auditevents | 顯示應⽤用暴暴露露的審計事件(如認證進⼊入、訂單失敗) |
GET | /beans | 描述應⽤用程式上下⽂文⾥裡里全部的 Bean 以及它們的關系 |
GET | /conditions | 就是 1.0 的 /autoconfig,提供⼀一份⾃自動配置⽣生效的條件情況,記錄哪些 ⾃自動配置條件通過了了,哪些沒通過 |
GET | /configprops | 描述配置屬性(包含預設值)如何注⼊入 Bean |
GET | /env | 擷取全部環境屬性 |
GET | /env/{name} | 根據名稱擷取特定的環境屬性值 |
GET | /flyway | 提供⼀一份 Flyway 資料庫遷移資訊 |
GET | /liquidbase | 顯示 Liquibase 資料庫遷移的纖細資訊 |
GET | /health | 報告應⽤用程式的健康名額,這些值由 HealthIndicator 的實作類提供 |
GET | /heapdump | dump ⼀一份應⽤用的 JVM 堆資訊 |
GET | /httptrace | 顯示 HTTP ⾜足迹,最近 100 個 HTTP request/repsponse |
GET | /info | 擷取應⽤用程式的定制資訊,這些資訊由 info 打頭的屬性提供 |
GET | /logfile | 傳回 log file 中的内容(如果 logging.file 或者 logging.path 被設定) |
GET | /loggers | 顯示和修改配置的 loggers |
GET | /metrics | 報告各種應⽤用程式度量量資訊,⽐比如記憶體⽤用量量和 HTTP 請求計數 |
GET | /metrics/{name} | 報告指定名稱的應⽤用程式度量量值 |
GET | /scheduledtasks | 展示應⽤用中的定時任務資訊 |
GET | /sessions | 如果我們使⽤用了了 Spring Session 展示應⽤用中的 HTTP Sessions 資訊 |
POST | /shutdown | 關閉應⽤用程式,要求 endpoints.shutdown.enabled 設定為 true |
GET | /mappings | 描述全部的 URI 路路徑,以及它們和控制器器(包含 Actuator 端點)的映射關系 |
GET | /threaddump | 擷取線程活動的快照 |
指令詳解
在 Spring Boot 2.x 中為了安全期間, Actuator 隻開放了兩個端點 /actuator/health 和 /actuator/info ,可以在 配置⽂件中設定打開。 可以打開所有的監控點
management.endpoints.web.exposure.include=*
也可以選擇打開部分:
management.endpoints.web.exposure.exclude=beans,trace
Actuator 預設所有的監控點路徑都在 /actuator/* ,當然如果有需要這個路徑也⽀持定制。
management.endpoints.web.base-path=/manage
設定完重新開機後,再次通路位址就會變成 /manage/* 。 Actuator ⼏乎監控了應⽤涉及的⽅⽅⾯⾯,我們重點講述⼀些經常在項⽬中常⽤的指令。
health
health 主要⽤來檢查應⽤的運⾏狀态,這是我們使⽤最⾼頻的⼀個監控點,通常使⽤此接⼝提醒我們應⽤實 例的運⾏狀态,以及應⽤不 “ 健康 ” 的原因,如資料庫連接配接、磁盤空間不夠等。 預設情況下 health 的狀态是開放的,添加依賴後啟動項⽬,通路: http://localhost:8080/actuator/health 即可 看到應⽤的狀态。
{
"status" : "UP"
}
預設情況下,最終的 Spring Boot 應⽤的狀态是由 HealthAggregator 彙總⽽成的,彙總的算法是:
- 設定狀态碼順序為 setStatusOrder(Status.DOWN, Status.OUTOFSERVICE, Status.UP, Status.UNKNOWN);
- 過濾掉不能識别的狀态碼
- 如果⽆任何狀态碼,整個 Spring Boot 應⽤的狀态是 UNKNOWN
- 将所有收集到的狀态碼按照 1 中的順序排序
- 傳回有序狀态碼序列中的第⼀個狀态碼,作為整個 Spring Boot 應⽤的狀态
health 通過合并⼏個健康指數檢查應⽤的健康情況。 Spring Boot Actuator 有⼏個預定義的健康名額⽐如 DataSourceHealthIndicator 、 DiskSpaceHealthIndicator 、 MongoHealthIndicator 、 RedisHealthIndicator 等,它使⽤這些健康名額作為健康檢查的⼀部分。 舉個例⼦,如果你的應⽤使⽤ Redis , RedisHealthindicator 将被當作檢查的⼀部分;如果使⽤ MongoDB , GitChat 那麼 MongoHealthIndicator 将被當作檢查的⼀部分。 可以在配置⽂件中關閉特定的健康檢查名額,⽐如關閉 Redis 的健康檢查:
management.health.redise.enabled=false
預設所有的這些健康名額被當作健康檢查的⼀部分。
詳細的健康檢查資訊
預設隻是展示了簡單的 UP 和 DOWN 狀态,為了查詢更詳細的監控名額資訊,可以在配置⽂件中添加以下 資訊:
management.endpoint.health.show-details=always
重新開機後再次通路⽹址 http://localhost:8080/actuator/health ,傳回資訊如下:
"status": "UP",
"diskSpace": {
"status": "UP",
"total": 209715195904,
"free": 183253909504,
"threshold": 10485760
}
}
可以看到 HealthEndPoint 給我們提供預設的監控結果,包含磁盤空間描述總磁盤空間,剩餘的磁盤空間和最 ⼩門檻值。 其實看 Spring Boot-actuator 源碼,會發現 HealthEndPoint 提供的資訊不僅限于此,在 org.springframework.boot.actuate.health 包下會發現 ElasticsearchHealthIndicator 、 RedisHealthIndicator 、 RabbitHealthIndicator 等。
info
info 是我們⾃⼰在配置⽂件中以 info 開頭的配置資訊,⽐如在示例項⽬中的配置是:
info.app.name=spring-boot-actuator
info.app.version= 1.0.0
info.app.test= test
啟動示例項⽬,通路 http://localhost:8080/actuator/info 傳回部分資訊如下:
{
"app": {
"name": "spring-boot-actuator",
"version": "1.0.0",
"test":"test"
}
}
beans
根據示例就可以看出,展示了 bean 的别名、類型、是否單例、類的位址、依賴等資訊。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/beans 傳回部分資訊如下:
[
{
"context": "application:8080:management",
"parent": "application:8080",
"beans": [
{
"bean": "embeddedServletContainerFactory",
"aliases": [
],
"scope": "singleton",
"type": "org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedSe
rvletContainerFactory",
"resource": "null",
"dependencies": [
]
},
{
"bean": "endpointWebMvcChildContextConfiguration",
"aliases": [
],
"scope": "singleton",
"type": "org.springframework.boot.actuate.autoconfigure.EndpointWebMvcChil
dContextConfiguration$$EnhancerBySpringCGLIB$$a4a10f9d",
"resource": "null",
"dependencies": [
]
}
}
]
conditions
Spring Boot 的⾃動配置功能⾮常便利,但有時候也意味着出問題⽐較難找出具體的原因。使⽤ conditions 可 以在應⽤運⾏時檢視代碼了解某個配置在什麼條件下⽣效,或者某個⾃動配置為什麼沒有⽣效。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/conditions 傳回部分資訊如下:
{
"positiveMatches": {
"DevToolsDataSourceAutoConfiguration": {
"notMatched": [
{
"condition": "DevToolsDataSourceAutoConfiguration.DevToolsData
SourceCondition",
"message": "DevTools DataSource Condition did not find a singl
e DataSource bean"
}
],
"matched": [ ]
},
"RemoteDevToolsAutoConfiguration": {
"notMatched": [
{
"condition": "OnPropertyCondition",
"message": "@ConditionalOnProperty (spring.devtools.remote.sec
ret) did not find property 'secret'"
}
],
"matched": [
{
"condition": "OnClassCondition",
"message": "@ConditionalOnClass found required classes 'javax.
servlet.Filter', 'org.springframework.http.server.ServerHttpRequest'; @Conditional
OnMissingClass did not find unwanted class"
}
]
}
}
}
confifigprops
檢視配置⽂件中設定的屬性内容以及⼀些配置屬性的預設值。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/confifigprops 傳回部分資訊如下:
{
...
"environmentEndpoint": {
"prefix": "endpoints.env",
"properties": {
"id": "env",
"sensitive": true,
"enabled": true
}
},
"spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartPrope
rties": {
"prefix": "spring.http.multipart",
"properties": {
"maxRequestSize": "10MB",
"fileSizeThreshold": "0",
"location": null,
"maxFileSize": "1MB",
"enabled": true,
"resolveLazily": false
}
},
"infoEndpoint": {
"prefix": "endpoints.info",
"properties": {
"id": "info",
"sensitive": false,
"enabled": true
}
}
...
}
env
展示了系統環境變量的配置資訊,包括使⽤的環境變量、 JVM 屬性、指令⾏參數、項⽬使⽤的 jar 包等信 息。和 confifigprops 不同的是, confifigprops 關注于配置資訊, env 關注運⾏環境資訊。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/env 傳回部分資訊如下:
{
"profiles": [
],
"server.ports": {
"local.management.port": 8088,
"local.server.port": 8080
},
"servletContextInitParams": {
},
"systemProperties": {
"com.sun.management.jmxremote.authenticate": "false",
"java.runtime.name": "Java(TM) SE Runtime Environment",
"spring.output.ansi.enabled": "always",
"sun.boot.library.path": "C:\\Program Files\\Java\\jdk1.8.0_101\\jre\\bin",
"java.vm.version": "25.101-b13",
"java.vm.vendor": "Oracle Corporation",
"java.vendor.url": "http://java.oracle.com/",
"java.rmi.server.randomIDs": "true",
"path.separator": ";",
"java.vm.name": "Java HotSpot(TM) 64-Bit Server VM",
"file.encoding.pkg": "sun.io",
"user.country": "CN",
"user.script": "",
"sun.java.launcher": "SUN_STANDARD",
"sun.os.patch.level": "",
"PID": "5268",
"com.sun.management.jmxremote.port": "60093",
"java.vm.specification.name": "Java Virtual Machine Spe
為了避免敏感資訊暴露到 /env ⾥,所有名為 password 、 secret 、 key (或者名字中最後⼀段是這些)的屬性 在 /env ⾥都會加上 “*” 。舉個例⼦,如果有⼀個屬性名字是 database.password ,那麼它在 /env 中的顯示效 果是這樣的:
/env/{name} ⽤法
就是 env 的擴充可以擷取指定配置資訊,⽐如 http://localhost:8080/actuator/env/java.vm.version ,傳回 {"java.vm.version":"25.101-b13"} 。
heapdump
傳回⼀個 GZip 壓縮的 JVM 堆 dump 。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/heapdump 會⾃動⽣成⼀個 JVM 的堆⽂件 heapdump ,我們可以使⽤ JDK ⾃帶的 JVM 監控⼯具 VisualVM 打開此⽂件檢視記憶體快照。類似如下圖:
httptrace
該端點⽤來傳回基本的 HTTP 跟蹤資訊。預設情況下,跟蹤資訊的存儲采⽤ org.springframework.boot.actuate.trace.InMemoryTraceRepository 實作的記憶體⽅式,始終保留最近的 100 條請求記錄。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/httptrace ,傳回資訊如下:
{
"traces": [
{
"timestamp": "2018-11-21T12:42:25.333Z",
"principal": null,
"session": null,
"request": {
"method": "GET",
"uri": "http://localhost:8080/actuator/heapdump",
"headers": {
"cookie": [
"Hm_lvt_0fb30c642c5f6453f17d881f529a1141=1513076406,151496
1720,1515649377; Hm_lvt_6d8e8bb59814010152d98507a18ad229=1515247964,1515296008,151
5672972,1516086283; UM_distinctid=1647364371ef6-003ab9d0469ea5-b7a103e-100200-1647
364371f104; CNZZDATA1260945749=232252692-1513233181-%7C1537492730"
],
"accept-language": [
"zh-CN,zh;q=0.9"
],
GitChat
"upgrade-insecure-requests": [
"1"
],
"host": [
"localhost:8080"
],
"connection": [
"keep-alive"
],
"accept-encoding": [
"gzip, deflate, br"
],
"accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,ima
ge/webp,image/apng,*/*;q=0.8"
],
"user-agent": [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537
.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36"
]
},
"remoteAddress": null
},
"response": {
"status": 200,
"headers": {
"Accept-Ranges": [
"bytes"
],
"Content-Length": [
"39454385"
],
"Date": [
"Wed, 21 Nov 2018 12:42:25 GMT"
],
"Content-Type": [
"application/octet-stream"
]
}
},
"timeTaken": 1380
},
{
...
},
...
]
}
記錄了請求的整個過程的詳細資訊。
metrics
最重要的監控内容之⼀,主要監控了 JVM 内容使⽤、 GC 情況、類加載資訊等。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/metrics 傳回部分資訊如下:
{
"mem": 337132,
"mem.free": 183380,
"processors": 4,
"instance.uptime": 254552,
"uptime": 259702,
"systemload.average": -1.0,
"heap.committed": 292864,
"heap.init": 129024,
"heap.used": 109483,
"heap": 1827840,
"nonheap.committed": 45248,
"nonheap.init": 2496,
"nonheap.used": 44269,
"nonheap": 0,
"threads.peak": 63,
"threads.daemon": 43,
"threads.totalStarted": 83,
"threads": 46,
"classes": 6357,
"classes.loaded": 6357,
"classes.unloaded": 0,
"gc.ps_scavenge.count": 8,
"gc.ps_scavenge.time": 99,
"gc.ps_marksweep.count": 1,
"gc.ps_marksweep.time": 43,
"httpsessions.max": -1,
"httpsessions.active": 0
}
對 /metrics 接⼝提供的資訊進⾏簡單分類如下表:
分類 | 字首 | 報告内容 |
垃圾收集器器 | gc.* | 已經發⽣生過的垃圾收集次數,以及垃圾收集所耗費的時間,适⽤用于标記—清理理垃圾收集器器和并⾏行行垃圾收集器器(資料源⾃自java.lang.management.GarbageCollectorMXBean) |
記憶體 | mem.* | 配置設定給應⽤用程式的記憶體數量量和空閑的記憶體數量量(資料源⾃自 java.lang. Runtime) |
堆 | heap.* | 目前記憶體⽤用量量(資料源⾃自 java.lang.management.MemoryUsage) |
類加載器器 | classes.* | JVM 類加載器器加載與解除安裝的類的數量量(資料源⾃自 java.lang. management.ClassLoadingMXBean) |
系統 | processors、instance.uptime、uptime、systemload.average | 系統資訊,如處理理器器數量量(資料源⾃自 java.lang.Runtime、運⾏行行時間 (資料源⾃自 java.lang.management.RuntimeMXBean)、平均負載 (資料源⾃自 java.lang.management.OperatingSystemMXBean) |
線程池 | thread.* | 線程、守護線程的數量量,以及 JVM 啟動後的線程數量量峰值(資料源 ⾃ 自 java.lang .management.ThreadMXBean) |
資料源 | datasource.* | 資料源連接配接的數量量(源⾃自資料源的中繼資料,僅當 Spring 應⽤用程式上下⽂文⾥裡里存在 DataSource Bean 的時候才會有這個資訊) |
Tomcat 會話 | httpsessions.* | Tomcat 的活躍會話數和最⼤大會話數(資料源⾃自嵌⼊入式 Tomcat 的Bean,僅在使⽤用嵌⼊入式 Tomcat 伺服器器運⾏行行應⽤用程式時才有這個資訊) |
HTTP | counter.status._、gauge.response._ | 多種應⽤用程式服務 HTTP 請求的度量量值與計數器器 |
解釋說明
- 請注意,這⾥的⼀些路徑成本,⽐如資料源和 Tomcat 會話,僅在應⽤程式中運⾏特定元件時才有資料, 還可以注冊⾃⼰的度量資訊。
- HTTP 的計數器和路徑成本需要做⼀點說明,counter.status 後的值是 HTTP 狀态碼,随後是所請求的路 徑。舉個例⼦,counter.status.200.metrics 表明 /metrics 端點傳回 200(OK) 狀态碼的次數。
- HTTP 的度量資訊在結構上也差不多,卻在報告另⼀類資訊。它們全部以 gauge.response 開頭,表明 這是 HTTP 響應的度量資訊,字首後是對應的路徑,路徑成本是以毫秒為機關的時間,反映了最近處理該 路徑請求的耗時。
- 這⾥還有⼏個特殊的值需要注意,root 路徑指向的是根路徑或/,star-star 代表了那些 Spring 認為是靜 态資源的路徑,包括圖⽚、JavaScript 和樣式表,其中還包含了那些找不到的資源,這就是為什麼經常 會看到 counter.status.404.star-star,因為傳回了 HTTP 404 (NOT FOUND) 狀态的請求數。
- /metrics 接⼝會傳回所有的可⽤路徑成本,但你也可能隻對某個值感興趣。要擷取單個值,請求時可以在GitChat URL 後加上對應的鍵名。例如,要檢視空閑記憶體⼤⼩,可以向 /metrics/mem.free 發⼀個GET請求,⽐ 如通路⽹址 http://localhost:8080/actuator/metrics/mem.free,傳回:{"mem.free":178123}。
shutdown
開啟接⼝優雅關閉 Spring Boot 應⽤,要使⽤這個功能⾸先需要在配置⽂件中開啟:
management.endpoint.shutdown.enabled=true
配置完成之後,啟動示例項⽬,使⽤ curl 模拟 post 請求通路 shutdown 接⼝。 shutdown 接⼝預設隻⽀持 post 請求。
curl -X POST "http://localhost:8080/actuator/shutdown"
{
"message": "Shutting down, bye..."
}
此時會發現應⽤已經被關閉。
mappings
描述全部的 URI 路徑,以及它們和控制器的映射關系。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/mappings 傳回部分資訊如下:
{
"/**/favicon.ico": {
"bean": "faviconHandlerMapping"
},
"{[/hello]}": {
"bean": "requestMappingHandlerMapping",
"method": "public java.lang.String com.neo.controller.HelloController.index()"
},
"{[/error]}": {
"bean": "requestMappingHandlerMapping",
"method": "public org.springframework.http.ResponseEntity<java.util.Map<java.l
ang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErr
orController.error(javax.servlet.http.HttpServletRequest)"
}
}
threaddump
/threaddump 接⼝會⽣成目前線程活動的快照,這個功能⾮常好,⽅便我們在⽇常定位問題的時候檢視線程 GitChat 的情況,主要展示了線程名、線程 ID 、線程的狀态、是否等待鎖資源等資訊。 啟動示例項⽬,通路⽹址 http://localhost:8080/actuator/threaddump 傳回部分資訊如下:
{
"threadName": "http-nio-8088-exec-6",
"threadId": 49,
"blockedTime": -1,
"blockedCount": 0,
"waitedTime": -1,
"waitedCount": 2,
"lockName": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionOb
[email protected]",
"lockOwnerId": -1,
"lockOwnerName": null,
"inNative": false,
"suspended": false,
"threadState": "WAITING",
"stackTrace": [
{
"methodName": "park",
"fileName": "Unsafe.java",
"lineNumber": -2,
"className": "sun.misc.Unsafe",
"nativeMethod": true
},
...
{
"methodName": "run",
"fileName": "TaskThread.java",
"lineNumber": 61,
"className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable",
"nativeMethod": false
}
...
],
"lockInfo": {
"className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$Conditio
nObject",
"identityHashCode": 372286721
}
}
...
]
⽣産出現問題的時候,可以通過應⽤的線程快照來檢測應⽤正在執⾏的任務。 GitChat
總結
經過這節課實踐我們發現 Spring Boot Actuator 作為 Spring Boot ⾃帶的監控元件⾮常強⼤,它可以監控和管 理 Spring Boot 應⽤,如健康檢查、審計、統計和 HTTP 追蹤等,所有的這些特性可以通過 JMX 或者 HTTP endpoints 來獲得。使⽤ Spring Boot Actuator 之後,可以讓我們通過很簡單的接⼝或者指令來查詢應⽤的⼀ 切運⾏狀态,從⽽達到對應⽤的運⾏情況了如指掌。 點選這⾥下載下傳源碼