springboot(十九):使用Spring Boot Actuator監控應用
微服務的特點決定了功能模塊的部署是分布式的,大部分功能模塊都是運行在不同的機器上,彼此通過服務調用進行交互,前後臺的業務流會經過很多個微服務的處理和傳遞,出現了異常如何快速定位是哪個環節出現了問題?
在這種框架下,微服務的監控顯得尤為重要。本文主要結合Spring Boot Actuator,跟大家一起分享微服務Spring Boot Actuator的常見用法,方便我們在日常中對我們的微服務進行監控治理。
Actuator監控
Spring Boot使用“習慣優於配置的理念”,采用包掃描和自動化配置的機制來加載依賴jar中的Spring bean,不需要任何Xml配置,就可以實現Spring的所有配置。雖然這樣做能讓我們的代碼變得非常簡潔,但是整個應用的實例創建和依賴關系等信息都被離散到了各個配置類的註解上,這使得我們分析整個應用中資源和實例的各種關系變得非常的困難。
Actuator是Spring Boot提供的對應用系統的自省和監控的集成功能,可以查看應用配置的詳細信息,例如自動化配置信息、創建的Spring beans以及一些環境屬性等。
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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
為了保證actuator暴露的監控接口的安全性,需要添加安全控制的依賴spring-boot-start-security依賴,訪問應用監控端點時,都需要輸入驗證信息。Security依賴,可以選擇不加,不進行安全管理,但不建議這麽做。
Actuator 的 REST 接口
Actuator監控分成兩類:原生端點和用戶自定義端點;自定義端點主要是指擴展性,用戶可以根據自己的實際應用,定義一些比較關心的指標,在運行期進行監控。
原生端點是在應用程序裏提供眾多 Web 接口,通過它們了解應用程序運行時的內部狀況。原生端點又可以分成三類:
- 應用配置類:可以查看應用在運行期的靜態信息:例如自動配置信息、加載的springbean信息、yml文件配置信息、環境信息、請求映射信息;
- 度量指標類:主要是運行期的動態信息,例如堆棧、請求連、一些健康指標、metrics信息等;
- 操作控制類:主要是指shutdown,用戶可以發送一個請求將應用的監控功能關閉。
Actuator 提供了 13 個接口,具體如下表所示。
HTTP 方法 | 路徑 | 描述 |
---|---|---|
GET | /autoconfig | 提供了一份自動配置報告,記錄哪些自動配置條件通過了,哪些沒通過 |
GET | /configprops | 描述配置屬性(包含默認值)如何註入Bean |
GET | /beans | 描述應用程序上下文裏全部的Bean,以及它們的關系 |
GET | /dump | 獲取線程活動的快照 |
GET | /env | 獲取全部環境屬性 |
GET | /env/{name} | 根據名稱獲取特定的環境屬性值 |
GET | /health | 報告應用程序的健康指標,這些值由HealthIndicator的實現類提供 |
GET | /info | 獲取應用程序的定制信息,這些信息由info打頭的屬性提供 |
GET | /mappings | 描述全部的URI路徑,以及它們和控制器(包含Actuator端點)的映射關系 |
GET | /metrics | 報告各種應用程序度量信息,比如內存用量和HTTP請求計數 |
GET | /metrics/{name} | 報告指定名稱的應用程序度量值 |
POST | /shutdown | 關閉應用程序,要求endpoints.shutdown.enabled設置為true |
GET | /trace | 提供基本的HTTP請求跟蹤信息(時間戳、HTTP頭等) |
快速上手
相關配置
項目依賴
<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>
配置文件
server:
port: 8080
management:
security:
enabled: false #關掉安全認證
port: 8088 #管理端口調整成8088
context-path: /monitor #actuator的訪問路徑
endpoints:
shutdown:
enabled: true
info:
app:
name: spring-boot-actuator
version: 1.0.0
management.security.enabled=false
默認有一部分信息需要安全驗證之後才可以查看,如果去掉這些安全認證,直接設置management.security.enabled=falsemanagement.context-path=/monitor
代表啟用單獨的url地址來監控Spring Boot應用,為了安全一般都啟用獨立的端口來訪問後端的監控信息endpoints.shutdown.enabled=true
啟用接口關閉Spring Boot
配置完成之後,啟動項目就可以繼續驗證各個監控功能了。
命令詳解
autoconfig
Spring Boot的自動配置功能非常便利,但有時候也意味著出問題比較難找出具體的原因。使用 autoconfig 可以在應用運行時查看代碼了某個配置在什麽條件下生效,或者某個自動配置為什麽沒有生效。
啟動示例項目,訪問:http://localhost:8088/monitor/autoconfig
返回部分信息如下:
{
"positiveMatches": {
"DevToolsDataSourceAutoConfiguration": {
"notMatched": [
{
"condition": "DevToolsDataSourceAutoConfiguration.DevToolsDataSourceCondition",
"message": "DevTools DataSource Condition did not find a single DataSource bean"
}
],
"matched": [ ]
},
"RemoteDevToolsAutoConfiguration": {
"notMatched": [
{
"condition": "OnPropertyCondition",
"message": "@ConditionalOnProperty (spring.devtools.remote.secret) did not find property 'secret'"
}
],
"matched": [
{
"condition": "OnClassCondition",
"message": "@ConditionalOnClass found required classes 'javax.servlet.Filter', 'org.springframework.http.server.ServerHttpRequest'; @ConditionalOnMissingClass did not find unwanted class"
}
]
}
}
}
configprops
查看配置文件中設置的屬性內容,以及一些配置屬性的默認值。
啟動示例項目,訪問:http://localhost:8088/monitor/configprops
返回部分信息如下:
{
...
"environmentEndpoint": {
"prefix": "endpoints.env",
"properties": {
"id": "env",
"sensitive": true,
"enabled": true
}
},
"spring.http.multipart-org.springframework.boot.autoconfigure.web.MultipartProperties": {
"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
}
}
...
}
beans
根據示例就可以看出,展示了bean的別名、類型、是否單例、類的地址、依賴等信息。
啟動示例項目,訪問:http://localhost:8088/monitor/beans
返回部分信息如下:
[
{
"context": "application:8080:management",
"parent": "application:8080",
"beans": [
{
"bean": "embeddedServletContainerFactory",
"aliases": [
],
"scope": "singleton",
"type": "org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory",
"resource": "null",
"dependencies": [
]
},
{
"bean": "endpointWebMvcChildContextConfiguration",
"aliases": [
],
"scope": "singleton",
"type": "org.springframework.boot.actuate.autoconfigure.EndpointWebMvcChildContextConfiguration$$EnhancerBySpringCGLIB$$a4a10f9d",
"resource": "null",
"dependencies": [
]
}
}
]
dump
/dump 接口會生成當前線程活動的快照。這個功能非常好,方便我們在日常定位問題的時候查看線程的情況。
主要展示了線程名、線程ID、線程的狀態、是否等待鎖資源等信息。
啟動示例項目,訪問:http://localhost:8088/monitor/dump
返回部分信息如下:
[
{
"threadName": "http-nio-8088-exec-6",
"threadId": 49,
"blockedTime": -1,
"blockedCount": 0,
"waitedTime": -1,
"waitedCount": 2,
"lockName": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@1630a501",
"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": "park",
"fileName": "LockSupport.java",
"lineNumber": 175,
"className": "java.util.concurrent.locks.LockSupport",
"nativeMethod": false
},
{
"methodName": "await",
"fileName": "AbstractQueuedSynchronizer.java",
"lineNumber": 2039,
"className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject",
"nativeMethod": false
},
...
{
"methodName": "getTask",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 1067,
"className": "java.util.concurrent.ThreadPoolExecutor",
"nativeMethod": false
},
{
"methodName": "runWorker",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 1127,
"className": "java.util.concurrent.ThreadPoolExecutor",
"nativeMethod": false
},
{
"methodName": "run",
"fileName": "ThreadPoolExecutor.java",
"lineNumber": 617,
"className": "java.util.concurrent.ThreadPoolExecutor$Worker",
"nativeMethod": false
},
{
"methodName": "run",
"fileName": "TaskThread.java",
"lineNumber": 61,
"className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable",
"nativeMethod": false
},
{
"methodName": "run",
"fileName": "Thread.java",
"lineNumber": 745,
"className": "java.lang.Thread",
"nativeMethod": false
}
],
"lockedMonitors": [
],
"lockedSynchronizers": [
],
"lockInfo": {
"className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject",
"identityHashCode": 372286721
}
}
...
]
env
展示了系統環境變量的配置信息,包括使用的環境變量、JVM 屬性、命令行參數、項目使用的jar包等信息。和configprops不同的是,configprops關註於配置信息,env關註運行環境信息。
啟動示例項目,訪問:http://localhost:8088/monitor/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中的顯示效果是這樣的:
"database.password":"******"
/env/{name}用法
就是env的擴展 可以獲取指定配置信息,比如:http://localhost:8088/monitor/env/java.vm.version
,返回:{"java.vm.version":"25.101-b13"}
health
可以看到 HealthEndPoint 給我們提供默認的監控結果,包含 磁盤檢測和數據庫檢測
啟動示例項目,訪問:http://localhost:8088/monitor/health
返回部分信息,下面的JSON響應是由狀態、磁盤空間和db。描述了應用程序的整體健康狀態,UP 表明應用程序是健康的。磁盤空間描述總磁盤空間,剩余的磁盤空間和最小閾值。application.properties
閾值是可配置的
{
"status": "UP",
"diskSpace": {
"status": "UP",
"total": 209715195904,
"free": 183253909504,
"threshold": 10485760
}
"db": {
"status": "UP",
"database": "MySQL",
"hello": 1
}
}
其實看 Spring Boot-actuator 源碼,你會發現 HealthEndPoint 提供的信息不僅限於此,org.springframework.boot.actuate.health 包下 你會發現 ElasticsearchHealthIndicator、RedisHealthIndicator、RabbitHealthIndicator 等
info
info就是我們自己配置在配置文件中以Info開頭的配置信息,比如我們在示例項目中的配置是:
info:
app:
name: spring-boot-actuator
version: 1.0.0
啟動示例項目,訪問:http://localhost:8088/monitor/info
返回部分信息如下:
{
"app": {
"name": "spring-boot-actuator",
"version": "1.0.0"
}
}
mappings
描述全部的URI路徑,以及它們和控制器的映射關系
啟動示例項目,訪問:http://localhost:8088/monitor/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.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)"
}
}
metrics
最重要的監控內容之一,主要監控了JVM內容使用、GC情況、類加載信息等。
啟動示例項目,訪問:http://localhost:8088/monitor/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
接口會返回所有的可用度量值,但你也可能只對某個值感興趣。要獲取單個值,請求時可以在URL後加上對應的鍵名。例如,要查看空閑內存大小,可以向/metrics/mem.free
發一 個GET請求。例如訪問:http://localhost:8088/monitor/metrics/mem.free
,返回:{"mem.free":178123}
。
shutdown
開啟接口優雅關閉Spring Boot應用,要使用這個功能首先需要在配置文件中開啟:
endpoints:
shutdown:
enabled: true
配置完成之後,啟動示例項目,訪問:http://localhost:8088/monitor/shutdown
返回部分信息如下:
{
"message": "Shutting down, bye..."
}
此時你會發現應用已經被關閉。
trace
/trace 接口能報告所有Web請求的詳細信息,包括請求方法、路徑、時間戳以及請求和響應的頭信息,記錄每一次請求的詳細信息。
啟動示例項目,先訪問一次:http://localhost:8080/hello
,再到瀏覽器執行:http://localhost:8088/monitor/trace
查看返回信息:
[
{
"timestamp": 1516780334777,
"info": {
"method": "GET",
"path": "/hello",
"headers": {
"request": {
"host": "localhost:8080",
"connection": "keep-alive",
"cache-control": "max-age=0",
"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",
"upgrade-insecure-requests": "1",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"accept-encoding": "gzip, deflate, br",
"accept-language": "zh-CN,zh;q=0.9",
"cookie": "UM_distinctid=16053ba344f1cd-0dc220c44cc94-b7a103e-13c680-16053ba3450751; Hm_lvt_0fb30c642c5f6453f17d881f529a1141=1513076406,1514961720,1515649377; CNZZDATA1260945749=232252692-1513233181-%7C1516085149; Hm_lvt_6d8e8bb59814010152d98507a18ad229=1515247964,1515296008,1515672972,1516086283"
},
"response": {
"X-Application-Context": "application:8080",
"Content-Type": "text/html;charset=UTF-8",
"Content-Length": "11",
"Date": "Wed, 24 Jan 2018 07:52:14 GMT",
"status": "200"
}
},
"timeTaken": "4"
}
}
]
上述信息展示了,/hello請求的詳細信息。
其它配置
敏感信息訪問限制
根據上面表格,鑒權為false的,表示不敏感,可以隨意訪問,否則就是做了一些保護,不能隨意訪問。
endpoints.mappings.sensitive=false
這樣需要對每一個都設置,比較麻煩。敏感方法默認是需要用戶擁有ACTUATOR角色,因此,也可以設置關閉安全限制:
management.security.enabled=false
或者配合Spring Security做細粒度控制。
啟用和禁用接口
雖然Actuator的接口都很有用,但你不一定需要全部這些接口。默認情況下,所有接口(除 了/shutdown)都啟用。比如要禁用 /metrics 接口,則可以設置如下:
endpoints.metrics.enabled = false
如果你只想打開一兩個接口,那就先禁用全部接口,然後啟用那幾個你要的,這樣更方便。
endpoints.enabled = false
endpoints.metrics.enabled = true
參考
Spring Boot Actuator: Production-ready features
對沒有監控的微服務Say No!
Spring Boot Actuator 使用
springboot(十九):使用Spring Boot Actuator監控應用