面試官:聊一聊SpringBoot服務監控機制

雙子孤狼 發表於 2021-04-06

前言

任何一個服務如果沒有監控,那就是兩眼一抹黑,無法知道當前服務的執行情況,也就無法對可能出現的異常狀況進行很好的處理,所以對任意一個服務來說,監控都是必不可少的。

就目前而言,大部分微服務應用都是基於 SpringBoot 來構建,所以瞭解 SpringBoot 的監控特性是非常有必要的,而 SpringBoot 也提供了一些特性來幫助我們監控應用。

本文基於 SpringBoot 2.3.1.RELEASE 版本演示。

SpringBoot 監控

SpringBoot 中的監控可以分為 HTTP 端點和 JMX 兩種方式來監控當前應用的執行狀況和指標收集

HTTP Endpoints 監控

執行器端點允許您監視應用程式並與之互動。SpringBoot 包括許多內建的端點,並允許我們新增自己的端點。可以通過 HTTPJMX 啟用或禁用每個端點,並公開(使其可以遠端訪問)。每個端點都有一個唯一的 id,訪問時可以通過如下地址進行訪問:http:ip:port/{id}(SpringBoot 1.x ),而在 SpringBoot 2.x 版本中,預設新增了一個 /actuator 作為基本路,訪問地址則對應為 :http:ip:port/actuator/{id}

使用 HTTP 監控非常簡單,在 SpringBoot 專案中,引入如下依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>

預設就可以通過地址 http:localhost:8080/actuator/health,訪問之後得到如下結果:

image

SpringBoot 中提供了非常多的預設端點監控,但是出於安全考慮,預設情況下有些端點並不是開啟狀態,如 shutdown 端點就是預設關閉的。

內建端點

SpringBoot 中預設提供的常用內建端點如下:

端點 id 描述
auditevents 公開當前應用程式的審計事件資訊,需要 AuditEventRepository Bean。
beans 展示程式中所有的 Bean。
caches 公開可用的快取。
conditions 展示配置類或者自動裝配類中的條件,以及它們匹配或者不匹配的原因。
configprops 顯示所有 @ConfigurationProperties 中的配置屬性。
env 顯示 ConfigurableEnvironment 中的所有環境。
health 顯示應用程式執行狀況資訊。
httptrace 顯示 HTTP 跟蹤資訊(預設情況下統計最近 100 次請求),需要 HttpTraceRepository Bean。
info 顯示任意程式資訊。
integrationgraph 顯示 Spring 整合圖,需要依賴 spring-integration-core。
loggers 展示和修改應用中的 loggers 配置。
metrics 展示當前應用監控指標的度量。
mappings 展示所有 @RequestMapping 路徑。
scheduledtasks 展示應用中的所有定時任務資訊。
sessions 允許從 Spring 會話支援的會話儲存中檢索和刪除使用者會話。需要使用基於 Spring Session web應用程式。
shutdown 優雅的關閉程式,預設禁止了該端點的訪問。

雖然說這裡的大部分端點都是預設開啟的,但是預設暴露(允許對外訪問)的只有 healthinfo 端點,所以如果需要允許端點對外暴露,可以通過如下配置(如果想要暴露所有的端點,則可以直接配置 "*" ):

management:
  endpoints:
    web:
      exposure:
        include: [health,info,mappings] //或者直接配置 "*"

另外,開啟或禁用某一個端點,也可以通過通過如下配置進行動態控制:

management.endpoint.<id>.enabled=true

接下來我們挑選幾個重點的端點來介紹一下。

health 端點

health 斷點預設只是展示當前應用健康資訊,但是我們可以通過另一個配置開啟詳細資訊,這樣不僅僅會監控當前應用,還會監控與當前應用相關的其他第三方應用,如 Redis

management:
  endpoint:
    health:
      show-details: always

這個配置開啟之後,我們連線上 Redis 之後再次訪問 health 端點,就可以展示 Redis 服務的健康資訊了:

image

loggers 端點

訪問 http://localhost:8080/actuator/loggers 可以檢視當前應用的日誌級別等資訊:

image

這裡面本身並不特別,但是有一個功能卻非常有用,比如我們生產環境日誌級別一般都是 info,但是現在有一個 bug 通過 info 級別無法排查,那麼我們就可以臨時修改 log 級別。

比如上圖中的 ROOT 節點是 info 級別,那麼我們可以通過 postman 等工具來發一個 post 請求修改日誌級別。

image

修改之後就會發現,日誌由原來的 info 變成了 debug

image

metrics 端點

metrics 是一個非常重要的監控端點,其監控內容覆蓋了 JVM 記憶體、堆、類載入、處理器和 tomcat 容器等一些重要指標:

image

可以看到這裡麵包含了非常多的指標,任意訪問一個指標就可以檢視對應的指標資訊:

image

自定義監控端點

通過上面的介紹,可以看到 SpringBoot 提供的監控非常強大,但是就算再全面的監控也不可能滿足所有人的需求,所以 SpringBoot 也支援自定義監控端點。

自定義監控端點常用註解

自定義一個監控端點主要有如下常用註解:

  • @Endpoint:定義一個監控端點,同時支援 HTTPJMX 兩種方式。
  • @WebEndpoint:定義一個監控端點,只支援 HTTP 方式。
  • @JmxEndpoint:定義一個監控端點,只支援 JMX 方式。

以上三個註解作用在類上,表示當前類是一個監控端點,另外還有一些註解會用在方法和引數上:

  • @ReadOperation:作用在方法上,可用來返回端點展示的資訊(通過 Get 方法請求)。
  • @WriteOperation:作用在方法上,可用來修改端點展示的資訊(通過 Post 方法請求)。
  • @DeleteOperation:作用在方法上,可用來刪除對應端點資訊(通過 Delete 方法請求)。
  • @Selector:作用在引數上,用來定位一個端點的具體指標路由。

來,一起寫一個自己的監控端點

  • 定義一個類,並使用 @Endpoint 註解標註標識,同時定義幾個方法用 @ReadOperation@WriteOperation 註解來標註:
@Endpoint(id="myEndpoint")
@Component
public class MyEndpoint {
    private String STATUS = "up";
    private String DETAIL = "一切正常";

//    @ReadOperation
//    public String test1(){
//        return "wolf";
//    }

//    @ReadOperation
//    public Map<String,String> test2(){
//        Map<String,String> map = new HashMap();
//        map.put("status","up");
//        return map;
//    }

    @ReadOperation
    public JSONObject test3(){
        JSONObject jsonObject= new JSONObject();
        jsonObject.put("status",STATUS);
        jsonObject.put("detail",DETAIL);
        return jsonObject;
    }

    @ReadOperation
    public JSONObject test3_1(@Selector String name){
        JSONObject jsonObject= new JSONObject();
        if ("status".equals(name)){
            jsonObject.put("status",STATUS);
        }else if ("detail".equals(name)){
            jsonObject.put("detail",DETAIL);
        }
        return jsonObject;
    }

    @WriteOperation//動態修改指標
    public void test4(@Selector String name,@Nullable String value){
        if (!StringUtils.isEmpty(value)){
            if ("status".equals(name)){
                STATUS = value;
            }else if ("detail".equals(name)){
                DETAIL = value;
            }
        }
    }
}
  1. @Component 註解表示將該類交給 Spring 進行管理,或者也可以再定義一個 Configuration 類來載入該 Bean 也可以,當然,如果我們需要提供給第三方使用,如果無法保證當前包名被掃描,則需要使用 SpringBoot 的自動裝配機制將該類進行管理。
  2. @ReadOperation 方法可以返回 String 或者 JSONObject 或者 Map 集合等。
  3. 引數上加了 @Selector 註解則表示訪問斷端點的時候可以直接訪問子節點。

完成了上面的類,啟動 SpringBoot 應用,接下來就可以直接通過 http://localhost:8080/actuator/myEndpoint 進行訪問了:

image

同時,因為 test3_1 方法使用了 @Selector 註解,所以我們可以通過這個方法每一個指標的明細:

image

而帶有 @WriteOperation 註解的方法可以用來修改指標,這個方法需要用 post 進行訪問,訪問的引數可以直接使用字串傳參,也可以直接使用 json 進行傳參,修改之後再次檢視就可以發現指標已經被動態修改:

image

JMX 監控

JMX 全稱為 Java Management Extensions,即 Java 管理擴充套件。它提供了對 Java 應用程式和 JVM 的監控管理。通過JMX 我們可以監控伺服器中各種資源的使用情況以及執行緒,記憶體和 CPU 等使用情況。

開啟 jdk 下提供的工具 jConsole

image

開啟之後這裡會監控到我們已經啟動的應用,雙擊進入:

image

如何手動註冊一個 JMX MBean

  • 定義一個介面 SystemInfoMBean(注意名字必須要用 MBean 結尾):
public interface SystemInfoMBean {
    int getCpuCore();
    long getTotalMemory();
    void shutdown();
}
  • 再定義一個類實現 SystemInfoMBean 介面,實現類的明明方式為介面名去掉 MBean
public class SystemInfo implements SystemInfoMBean {
    @Override
    public int getCpuCore() {
        return Runtime.getRuntime().availableProcessors();
    }
    @Override
    public long getTotalMemory() {
        return Runtime.getRuntime().totalMemory();
    }

    @Override
    public void shutdown() {
        System.exit(0);
    }
}
  • 最後就是需要將該實現類進行註冊:
public class JmxRegisterMain {
    public static void main(String[] args) throws NotCompliantMBeanException, InstanceAlreadyExistsException, MBeanRegistrationException, MalformedObjectNameException, IOException {
        MBeanServer mBeanServer= ManagementFactory.getPlatformMBeanServer();
        ObjectName objectName=new ObjectName("com.lonely.wolf.note.springboot.actuator.jmx:type=SystemInfo");
        SystemInfo SystemInfo =new SystemInfo();
        mBeanServer.registerMBean(SystemInfo,objectName);//註冊
        System.in.read();//防止程式結束
    }
}

執行該 main 方法,再開啟 jConsole 就可以看到成功註冊了一個 MBean

image

同樣的,Spring 當中只要我們使用了 @@Endpoint 或者 @JmxEndpoint 註解,就會自動幫我們註冊一個 MBean,其原理也是利用了自動裝配機制。

其他監控

除了 SpringBoot 自帶的監控之外,也有其他第三方開源的強大監控系統,如 Prometheus,而且 SpringBoot 也將其進行了整合,使用 Prometheus 時只需要引入如下 jar 包即可:

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

當然,如果使用 Prometheus 的話需要單獨安裝,而且一般會選擇 Prometheus + Grafana 來共同實現一個監控平臺,在這裡就不做過多介紹,如果感興趣的朋友可以自己去了解下這兩種軟體的使用。

總結

本文主要講述了 Spring Boot actuator 的使用,並分別介紹了其中兩種監控型別 HTTPJMX,最後通過一個例子來實現了自定義的端點,同時也實現了手動註冊一個 MBean 的方法。