SpringBoot詳解整合Spring Boot Admin實現監控功能

監控

​ 在說監控之前,需要回顧一下軟件業的發展史。最早的軟件完成一些非常簡單的功能,代碼不多,錯誤也少。隨著軟件功能的逐步完善,軟件的功能變得越來越復雜,功能不能得到有效的保障,這個階段出現瞭針對軟件功能的檢測,也就是軟件測試。伴隨著計算機操作系統的逐步升級,軟件的運行狀態也變得開始讓人捉摸不透,出現瞭不穩定的狀況。伴隨著計算機網絡的發展,程序也從單機狀態切換成基於計算機網絡的程序,應用於網絡的程序開始出現,由於網絡的不穩定性,程序的運行狀態讓使用者更加堪憂。互聯網的出現徹底打破瞭軟件的思維模式,隨之而來的互聯網軟件就更加凸顯出應對各種各樣復雜的網絡情況之下的弱小。計算機軟件的運行狀況已經成為瞭軟件運行的一個大話題,針對軟件的運行狀況就出現瞭全新的思維,建立起瞭初代的軟件運行狀態監控。

​ 什麼是監控?就是通過軟件的方式展示另一個軟件的運行情況,運行的情況則通過各種各樣的指標數據反饋給監控人員。例如網絡是否順暢、服務器是否在運行、程序的功能是否能夠整百分百運行成功,內存是否夠用,等等等等。

​ 本章要講解的監控就是對軟件的運行情況進行監督,但是springboot程序與非springboot程序的差異還是很大的,為瞭方便監控軟件的開發,springboot提供瞭一套功能接口,為開發者加速開發過程。

監控的意義

​ 對於現代的互聯網程序來說,規模越來越大,功能越來越復雜,還要追求更好的客戶體驗,因此要監控的信息量也就比較大瞭。由於現在的互聯網程序大部分都是基於微服務的程序,一個程序的運行需要若幹個服務來保障,因此第一個要監控的指標就是服務是否正常運行,也就是監控服務狀態是否處理宕機狀態。一旦發現某個服務宕機瞭,必須馬上給出對應的解決方案,避免整體應用功能受影響。其次,由於互聯網程序服務的客戶量是巨大的,當客戶的請求在短時間內集中達到服務器後,就會出現各種程序運行指標的波動。比如內存占用嚴重,請求無法及時響應處理等,這就是第二個要監控的重要指標,監控服務運行指標。雖然軟件是對外提供用戶的訪問需求,完成對應功能的,但是後臺的運行是否平穩,是否出現瞭不影響客戶使用的功能隱患,這些也是要密切監控的,此時就需要在不停機的情況下,監控系統運行情況,日志是一個不錯的手段。如果在眾多日志中找到開發者或運維人員所關註的日志信息,簡單快速有效的過濾出要看的日志也是監控系統需要考慮的問題,這就是第三個要監控的指標,監控程序運行日志。雖然我們期望程序一直平穩運行,但是由於突發情況的出現,例如服務器被攻擊、服務器內存溢出等情況造成瞭服務器宕機,此時當前服務不能滿足使用需要,就要將其重啟甚至關閉,如果快速控制服務器的啟停也是程序運行過程中不可回避的問題,這就是第四個監控項,管理服務狀態。以上這些僅僅是從大的方面來思考監控這個問題,還有很多的細節點,例如上線瞭一個新功能,定時提醒用戶續費,這種功能不是上線後馬上就運行的,但是當前功能是否真的啟動,如果快速的查詢到這個功能已經開啟,這也是監控中要解決的問題,等等。看來監控真的是一項非常重要的工作。

​ 通過上述描述,可以看出監控很重要。那具體的監控要如何開展呢?還要從實際的程序運行角度出發。比如現在有3個服務支撐著一個程序的運行,每個服務都有自己的運行狀態。

​ 此時被監控的信息就要在三個不同的程序中去查詢並展示,但是三個服務是服務於一個程序的運行的,如果不能合並到一個平臺上展示,監控工作量巨大,而且信息對稱性差,要不停的在三個監控端查看數據。如果將業務放大成30個,300個,3000個呢?看來必須有一個單獨的平臺,將多個被監控的服務對應的監控指標信息匯總在一起,這樣更利於監控工作的開展。

​ 新的程序專門用來監控,新的問題就出現瞭,是被監控程序主動上報信息還是監控程序主動獲取信息?如果監控程序不能主動獲取信息,這就意味著監控程序有可能看到的是很久之前被監控程序上報的信息,萬一被監控程序宕機瞭,監控程序就無法區分究竟是好久沒法信息瞭,還是已經下線瞭。所以監控程序必須具有主動發起請求獲取被監控服務信息的能力。

​ 如果監控程序要監控服務時,主動獲取對方的信息。那監控程序如何知道哪些程序被自己監控呢?不可能在監控程序中設置我監控誰,這樣互聯網上的所有程序豈不是都可以被監控到,這樣的話信息安全將無法得到保障。合理的做法隻能是在被監控程序啟動時上報監控程序,告訴監控程序你可以監控我瞭。看來需要在被監控程序端做主動上報的操作,這就要求被監控程序中配置對應的監控程序是誰。

​ 被監控程序可以提供各種各樣的指標數據給監控程序看,但是每一個指標都代表著公司的機密信息,並不是所有的指標都可以給任何人看的,乃至運維人員,所以對被監控指標的是否開放出來給監控系統看,也需要做詳細的設定。

​ 以上描述的整個過程就是一個監控系統的基本流程。

總結

  • 監控是一個非常重要的工作,是保障程序正常運行的基礎手段
  • 監控的過程通過一個監控程序進行,它匯總所有被監控的程序的信息集中統一展示
  • 被監控程序需要主動上報自己被監控,同時要設置哪些指標被監控

思考

​ 下面就要開始做監控瞭,新的問題就來瞭,監控程序怎麼做呢?難道要自己寫嗎?肯定是不現實的,如何進行監控,咱們下節再講。

可視化監控平臺

​ springboot抽取瞭大部分監控系統的常用指標,提出瞭監控的總思想。然後就有好心的同志根據監控的總思想,制作瞭一個通用性很強的監控系統,因為是基於springboot監控的核心思想制作的,所以這個程序被命名為Spring Boot Admin。

​ Spring Boot Admin,這是一個開源社區項目,用於管理和監控SpringBoot應用程序。這個項目中包含有客戶端和服務端兩部分,而監控平臺指的就是服務端。我們做的程序如果需要被監控,將我們做的程序制作成客戶端,然後配置服務端地址後,服務端就可以通過HTTP請求的方式從客戶端獲取對應的信息,並通過UI界面展示對應信息。

​ 下面就來開發這套監控程序,先制作服務端,其實服務端可以理解為是一個web程序,收到一些信息後展示這些信息。

服務端開發

步驟①:導入springboot admin對應的starter,版本與當前使用的springboot版本保持一致,並將其配置成web工程

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
    <version>2.5.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

​ 上述過程可以通過創建項目時使用勾選的形式完成。

步驟②:在引導類上添加註解@EnableAdminServer,聲明當前應用啟動後作為SpringBootAdmin的服務器使用

@SpringBootApplication
@EnableAdminServer
public class Springboot25AdminServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot25AdminServerApplication.class, args);
    }
}

​ 做到這裡,這個服務器就開發好瞭,啟動後就可以訪問當前程序瞭,界面如下。

​ 由於目前沒有啟動任何被監控的程序,所以裡面什麼信息都沒有。下面制作一個被監控的客戶端程序。

客戶端開發

​ 客戶端程序開發其實和服務端開發思路基本相似,多瞭一些配置而已。

步驟①:導入springboot admin對應的starter,版本與當前使用的springboot版本保持一致,並將其配置成web工程

<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.5.4</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

​ 上述過程也可以通過創建項目時使用勾選的形式完成,不過一定要小心,端口配置成不一樣的,否則會沖突。

步驟②:設置當前客戶端將信息上傳到哪個服務器上,通過yml文件配置

spring:
  boot:
    admin:
      client:
        url: http://localhost:8080

​ 做到這裡,這個客戶端就可以啟動瞭。啟動後再次訪問服務端程序,界面如下。

​ 可以看到,當前監控瞭1個程序,點擊進去查看詳細信息。

​ 由於當前沒有設置開放哪些信息給監控服務器,所以目前看不到什麼有效的信息。下面需要做兩組配置就可以看到信息瞭。

開放指定信息給服務器看

允許服務器以HTTP請求的方式獲取對應的信息

配置如下:

server:
  port: 80
spring:
  boot:
    admin:
      client:
        url: http://localhost:8080
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: "*"

​ 上述配置對於初學者來說比較容易混淆。簡單解釋一下,到下一節再做具體的講解。springbootadmin的客戶端默認開放瞭13組信息給服務器,但是這些信息除瞭一個之外,其他的信息都不讓通過HTTP請求查看。所以你看到的信息基本上就沒什麼內容瞭,隻能看到一個內容,就是下面的健康信息。

​ 但是即便如此我們看到健康信息中也沒什麼內容,原因在於健康信息中有一些信息描述瞭你當前應用使用瞭什麼技術等信息,如果無腦的對外暴露功能會有安全隱患。通過配置就可以開放所有的健康信息明細查看瞭。

management:
  endpoint:
    health:
      show-details: always

​ 健康明細信息如下:

​ 目前除瞭健康信息,其他信息都查閱不瞭。原因在於其他12種信息是默認不提供給服務器通過HTTP請求查閱的,所以需要開啟查閱的內容項,使用*表示查閱全部。記得帶引號。

endpoints:
  web:
    exposure:
      include: "*"

​ 配置後再刷新服務器頁面,就可以看到所有的信息瞭。

​ 以上界面中展示的信息量就非常大瞭,包含瞭13組信息,有性能指標監控,加載的bean列表,加載的系統屬性,日志的顯示控制等等。

配置多個客戶端

​ 可以通過配置客戶端的方式在其他的springboot程序中添加客戶端坐標,這樣當前服務器就可以監控多個客戶端程序瞭。每個客戶端展示不同的監控信息。

​ 進入監控面板,如果你加載的應用具有功能,在監控面板中可以看到3組信息展示的與之前加載的空工程不一樣。

類加載面板中可以查閱到開發者自定義的類:

映射中可以查閱到當前應用配置的所有請求

性能指標中可以查閱當前應用獨有的請求路徑統計數據

總結

  • 開發監控服務端需要導入坐標,然後在引導類上添加註解@EnableAdminServer,並將其配置成web程序即可
  • 開發被監控的客戶端需要導入坐標,然後配置服務端服務器地址,並做開放指標的設定即可
  • 在監控平臺中可以查閱到各種各樣被監控的指標,前提是客戶端開放瞭被監控的指標

思考

​ 之前說過,服務端要想監控客戶端,需要主動的獲取到對應信息並展示出來。但是目前我們並沒有在客戶端開發任何新的功能,但是服務端確可以獲取監控信息,誰幫我們做的這些功能呢?咱們下一節再講。

監控原理

​ 通過查閱監控中的映射指標,可以看到當前系統中可以運行的所有請求路徑,其中大部分路徑以/actuator開頭

​ 首先這些請求路徑不是開發者自己編寫的,其次這個路徑代表什麼含義呢?既然這個路徑可以訪問,就可以通過瀏覽器發送該請求看看究竟可以得到什麼信息。

​ 通過發送請求,可以得到一組json信息,如下

{
    "_links": {
        "self": {
            "href": "http://localhost:81/actuator",
            "templated": false
        },
        "beans": {
            "href": "http://localhost:81/actuator/beans",
            "templated": false
        },
        "caches-cache": {
            "href": "http://localhost:81/actuator/caches/{cache}",
            "templated": true
        },
        "caches": {
            "href": "http://localhost:81/actuator/caches",
            "templated": false
        },
        "health": {
            "href": "http://localhost:81/actuator/health",
            "templated": false
        },
        "health-path": {
            "href": "http://localhost:81/actuator/health/{*path}",
            "templated": true
        },
        "info": {
            "href": "http://localhost:81/actuator/info",
            "templated": false
        },
        "conditions": {
            "href": "http://localhost:81/actuator/conditions",
            "templated": false
        },
        "shutdown": {
            "href": "http://localhost:81/actuator/shutdown",
            "templated": false
        },
        "configprops": {
            "href": "http://localhost:81/actuator/configprops",
            "templated": false
        },
        "configprops-prefix": {
            "href": "http://localhost:81/actuator/configprops/{prefix}",
            "templated": true
        },
        "env": {
            "href": "http://localhost:81/actuator/env",
            "templated": false
        },
        "env-toMatch": {
            "href": "http://localhost:81/actuator/env/{toMatch}",
            "templated": true
        },
        "loggers": {
            "href": "http://localhost:81/actuator/loggers",
            "templated": false
        },
        "loggers-name": {
            "href": "http://localhost:81/actuator/loggers/{name}",
            "templated": true
        },
        "heapdump": {
            "href": "http://localhost:81/actuator/heapdump",
            "templated": false
        },
        "threaddump": {
            "href": "http://localhost:81/actuator/threaddump",
            "templated": false
        },
        "metrics-requiredMetricName": {
            "href": "http://localhost:81/actuator/metrics/{requiredMetricName}",
            "templated": true
        },
        "metrics": {
            "href": "http://localhost:81/actuator/metrics",
            "templated": false
        },
        "scheduledtasks": {
            "href": "http://localhost:81/actuator/scheduledtasks",
            "templated": false
        },
        "mappings": {
            "href": "http://localhost:81/actuator/mappings",
            "templated": false
        }
    }
}

​ 其中每一組數據都有一個請求路徑,而在這裡請求路徑中有之前看到過的health,發送此請求又得到瞭一組信息

{
    "status": "UP",
    "components": {
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 297042808832,
                "free": 72284409856,
                "threshold": 10485760,
                "exists": true
            }
        },
        "ping": {
            "status": "UP"
        }
    }
}

​ 當前信息與監控面板中的數據存在著對應關系

​ 原來監控中顯示的信息實際上是通過發送請求後得到json數據,然後展示出來。按照上述操作,可以發送更多的以/actuator開頭的鏈接地址,獲取更多的數據,這些數據匯總到一起組成瞭監控平臺顯示的所有數據。

​ 到這裡我們得到瞭一個核心信息,監控平臺中顯示的信息實際上是通過對被監控的應用發送請求得到的。那這些請求誰開發的呢?打開被監控應用的pom文件,其中導入瞭springboot admin的對應的client,在這個資源中導入瞭一個名稱叫做actuator的包。被監控的應用之所以可以對外提供上述請求路徑,就是因為添加瞭這個包。

​ 這個actuator是什麼呢?這就是本節要講的核心內容,監控的端點。

​ Actuator,可以稱為端點,描述瞭一組監控信息,SpringBootAdmin提供瞭多個內置端點,通過訪問端點就可以獲取對應的監控信息,也可以根據需要自定義端點信息。通過發送請求路勁**/actuator可以訪問應用所有端點信息,如果端點中還有明細信息可以發送請求/actuator/端點名稱**來獲取詳細信息。以下列出瞭所有端點信息說明:

用所有端點信息,如果端點中還有明細信息可以發送請求/actuator/端點名稱**來獲取詳細信息。以下列出瞭所有端點信息說明:

ID 描述 默認啟用
auditevents 暴露當前應用程序的審計事件信息。
beans 顯示應用程序中所有 Spring bean 的完整列表。
caches 暴露可用的緩存。
conditions 顯示在配置和自動配置類上評估的條件以及它們匹配或不匹配的原因。
configprops 顯示所有 @ConfigurationProperties 的校對清單。
env 暴露 Spring ConfigurableEnvironment 中的屬性。
flyway 顯示已應用的 Flyway 數據庫遷移。
health 顯示應用程序健康信息
httptrace 顯示 HTTP 追蹤信息(默認情況下,最後 100 個 HTTP 請求/響應交換)。
info 顯示應用程序信息。
integrationgraph 顯示 Spring Integration 圖。
loggers 顯示和修改應用程序中日志記錄器的配置。
liquibase 顯示已應用的 Liquibase 數據庫遷移。
metrics 顯示當前應用程序的指標度量信息。
mappings 顯示所有 @RequestMapping 路徑的整理清單。
scheduledtasks 顯示應用程序中的調度任務。
sessions 允許從 Spring Session 支持的會話存儲中檢索和刪除用戶會話。當使用 Spring Session 的響應式 Web 應用程序支持時不可用。
shutdown 正常關閉應用程序。
threaddump 執行線程 dump。
heapdump 返回一個 hprof 堆 dump 文件。
jolokia 通過 HTTP 暴露 JMX bean(當 Jolokia 在 classpath 上時,不適用於 WebFlux)。
logfile 返回日志文件的內容(如果已設置 logging.file 或 logging.path 屬性)。支持使用 HTTP Range 頭來檢索部分日志文件的內容。
prometheus 以可以由 Prometheus 服務器抓取的格式暴露指標。

​ 上述端點每一項代表被監控的指標,如果對外開放則監控平臺可以查詢到對應的端點信息,如果未開放則無法查詢對應的端點信息。通過配置可以設置端點是否對外開放功能。使用enable屬性控制端點是否對外開放。其中health端點為默認端點,不能關閉。

management:
  endpoint:
    health:                        # 端點名稱
      show-details: always
    #    監控端點關閉用如下方式
    info:                        # 端點名稱
      enabled: false                # 是否開放

​ 為瞭方便開發者快速配置端點,springboot admin設置瞭13個較為常用的端點作為默認開放的端點,如果需要控制默認開放的端點的開放狀態,可以通過配置設置,如下:

management:
  endpoints:
    enabled-by-default: true    # 是否開啟默認端點,默認值true

​ 上述端點開啟後,就可以通過端點對應的路徑查看對應的信息瞭。但是此時還不能通過HTTP請求查詢此信息,還需要開啟通過HTTP請求查詢的端點名稱,使用“*”可以簡化配置成開放所有端點的WEB端HTTP請求權限。

management:
  endpoints:
    web:
      exposure:
        include: "*"

​ 整體上來說,對於端點的配置有兩組信息,一組是endpoints開頭的,對所有端點進行配置,一組是endpoint開頭的,對具體端點進行配置。

management:
  endpoint:        # 具體端點的配置
    health:
      show-details: always
      #    監控端點關閉用如下方式
    info:
      enabled: false
  endpoints:    # 全部端點的配置
    web:
      exposure:
        include: "*"
    enabled-by-default: true

總結

  • 被監控客戶端通過添加actuator的坐標可以對外提供被訪問的端點功能
  • 端點功能的開放與關閉可以通過配置進行控制
  • web端默認無法獲取所有端點信息,通過配置開放端點功能

自定義監控指標

​ 端點描述瞭被監控的信息,除瞭系統默認的指標,還可以自行添加顯示的指標,下面就通過3種不同的端點的指標自定義方式來學習端點信息的二次開發。

INFO端點

​ info端點描述瞭當前應用的基本信息,可以通過兩種形式快速配置info端點的信息

配置形式

在yml文件中通過設置info節點的信息就可以快速配置端點信息

info:
  #appName: @project.artifactId@
  #version: @project.version@
  author: hashnode
  company: aaaaa

配置完畢後,對應信息顯示在監控平臺上

也可以通過請求端點信息路徑獲取對應json信息

編程形式

通過配置的形式隻能添加固定的數據,如果需要動態數據還可以通過配置bean的方式為info端點添加信息,此信息與配置信息共存

@Component
public class InfoConfig implements InfoContributor {
    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("runTime",System.currentTimeMillis());		//添加單個信息
        Map infoMap = new HashMap();		
        infoMap.put("buildTime","2022");
        builder.withDetails(infoMap);									//添加一組信息
    }
}

Health端點

​ health端點描述當前應用的運行健康指標,即應用的運行是否成功。通過編程的形式可以擴展指標信息。

@Component
public class HealthConfig extends AbstractHealthIndicator {
    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        boolean condition = true;
        if(condition) {
            builder.status(Status.UP);					//設置運行狀態為啟動狀態
            builder.withDetail("runTime", System.currentTimeMillis());
            Map infoMap = new HashMap();
            infoMap.put("buildTime", "2006");
            builder.withDetails(infoMap);
        }else{
            builder.status(Status.OUT_OF_SERVICE);		//設置運行狀態為不在服務狀態
            builder.withDetail("上線瞭嗎?","未上線");
        }
    }
}

​ 當任意一個組件狀態不為UP時,整體應用對外服務狀態為非UP狀態。

Metrics端點

​ metrics端點描述瞭性能指標,除瞭系統自帶的監控性能指標,還可以自定義性能指標。

@Service
public class BookServiceImpl extends ServiceImpl<BookDao, Book> implements IBookService {
    @Autowired
    private BookDao bookDao;

    private Counter counter;

    public BookServiceImpl(MeterRegistry meterRegistry){
        counter = meterRegistry.counter("用戶付費操作次數:");
    }
    @Override
    public boolean delete(Integer id) {
        //每次執行刪除業務等同於執行瞭付費業務
        counter.increment();
        return bookDao.deleteById(id) > 0;
    }
}

​ 在性能指標中就出現瞭自定義的性能指標監控項

自定義端點

​ 可以根據業務需要自定義端點,方便業務監控

@Component
@Endpoint(id="pay",enableByDefault = true)
public class PayEndpoint {
    @ReadOperation
    public Object getPay(){
        Map payMap = new HashMap();
        payMap.put("level 1","300");
        payMap.put("level 2","291");
        payMap.put("level 3","666");
        return payMap;
    }
}

​ 由於此端點數據spirng boot admin無法預知該如何展示,所以通過界面無法看到此數據,通過HTTP請求路徑可以獲取到當前端點的信息,但是需要先開啟當前端點對外功能,或者設置當前端點為默認開發的端點。

總結

  • 端點的指標可以自定義,但是每種不同的指標根據其功能不同,自定義方式不同
  • info端點通過配置和編程的方式都可以添加端點指標
  • health端點通過編程的方式添加端點指標,需要註意要為對應指標添加啟動狀態的邏輯設定
  • metrics指標通過在業務中添加監控操作設置指標
  • 可以自定義端點添加更多的指標

到此這篇關於SpringBoot詳解整合Spring Boot Admin實現監控功能的文章就介紹到這瞭,更多相關SpringBoot Spring Boot Admin內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: