1. 程式人生 > >使用springboot actuator監控應用

使用springboot 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=false
  • management.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.u[email protected]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