1. 程式人生 > >SpringBoot第十二集:度量指標監控與非同步呼叫(2020最新最易懂)

SpringBoot第十二集:度量指標監控與非同步呼叫(2020最新最易懂)

SpringBoot第十二集:度量指標監控與非同步呼叫(2020最新最易懂)

  Spring Boot Actuatorspring boot專案一個監控模組,提供了很多原生的端點,包含了對應用系統的自省和監控的整合功能,比如應用程式上下文裡全部的Bean、執行狀況檢查、健康指標、環境變數及各類重要度量指標等等,以圖形化介面的方式展示這些資訊,通過這些監控資訊,我們就能隨時瞭解應用的執行情況了。

  作用:可以通過監控執行狀態檢查獲取應用的執行狀態,潛在問題等。可以更具這些且在風險對專案進行優化,排除問題保證專案的執行。可以通過監控度量指標獲取應用記憶體佔用,執行緒數量,垃圾回收過程對專案進行深入分析。

一.Spring Boot Actuator的度量指標監控入門

1,準備工作

  在專案中引入Actuator依賴,該以來由SprngBoot官方提供。

1 <dependency>
2     <groupId>org.springframework.boot</groupId>
3     <artifactId>spring-boot-starter-actuator</artifactId>
4 </dependency>

  當專案中引入了Actuator依賴後,那麼當前的專案就表示擁有了度量指標監控能力了,啟動SpringBoot專案,控制檯如下:

 

 

 框起來的那段內容表示:SpringBoot監控已經預設暴露了兩個基礎端點,可供我們訪問獲取應用監控資料,基礎訪問路徑是"/actuator"

2,訪問基礎端點測試

  輸入訪問路徑:localhost:8080/actuator  結果如下:(為便於檢視,我已將JSON結果格式化)

 1 {
 2     "_links": {
 3         "self": {
 4             "href": "http://localhost:8080/actuator",  // 基礎訪問地址
 5             "templated": false
 6         },
 7         "health-path": {
 8             "href": "http://localhost:8080/actuator/health/{*path}",  
 9             "templated": true
10         },
11         "health": {
12             "href": "http://localhost:8080/actuator/health",  // 暴露的端點一 (訪問URL)
13             "templated": false
14         },
15         "info": {
16             "href": "http://localhost:8080/actuator/info",  // 暴露的端點二 (訪問URL)
17             "templated": false
18         }
19     }
20 }

  那麼這些暴露的URL訪問後分別代表什麼呢?Actuator提供了13個介面,可以分為三大類:配置介面、度量介面和其它介面,具體如下表:

HTTP 方法路徑描述
GET /autoconfig 提供了一份自動配置報告,記錄哪些自動配置條件通過了,哪些沒通過
GET /configprops

描述配置屬性(包含預設值)如何注入Bean

●prefix屬性代表 了屬性的配置字首
●properties代表 了各個屬性的名稱和值。

GET /beans

描述應用程式上下文裡全部的Bean,以及它們的關係

●bean:Bean的名稱
●scope:Bean的作用域
●type:Bean的Java型別
●reource:class檔案的具體路徑
●dependencies:依賴的Bean名稱

GET /dump 獲取執行緒活動的快照
GET /env 獲取全部環境屬性。包括:環境變數、JVM屬性.應用的配置配置。命令列中的引數等
GET /env/{name} 根據名稱獲取特定的環境屬性值。/env介面還能用來獲取單個屬性的值。只需要在請求時在/env/+屬性名即可。
GET /health 報告應用程式的健康指標(執行狀態),這些值由HealthIndicator的實現類提供.(預設暴露的介面)
GET /info

獲取應用程式的自定義的定製資訊,這些資訊由info打頭的屬性提供。預設情況下.該端點只會返回一個空的json內容,我們可以在application.properties配置檔案中通過info前騷來設定一些屬性(預設暴露的介面)

GET /mappings 描述全部的URI路徑,以及它們和控制器(包含Actuator端點)的對映關係,羅列出應用程式釋出的全部介面。

●bean屬性標識了 該對映關係的請求處理器
●method屬性標識了該對映關係的具體處理類和處理函式。

GET /metrics 報告各種應用程式度量資訊,比如記憶體用量,執行緒資訊,垃圾回收資訊,HTTP請求計數等
GET /metrics/{name} 報告指定名稱的應用程式度量值
POST /shutdown 關閉應用程式,要求endpoints.shutdown.enabled設定為true
GET /trace 提供基本的HTTP請求跟蹤資訊(時間戳、HTTP頭等)

  訪問一個試試?localhost:8080/actuator/health   訪問後結果:執行狀態為正常執行(說明:Actuator監控獲取的所有資料都是JSON格式的。)

1 {"status":"UP"}

  那麼如果希望訪問其他介面怎麼辦?,預設只開啟了info、health兩個端點,需要額外開啟端點,需要額外去配置的!!!

 1 #配置訪問端點的根路徑
 2 management: 
 3   endpoints:
 4     web:
 5       # 配置訪問端點的根路徑,預設是/actuator
 6       #base-path: /actuator
 7       exposure:
 8         #配置開啟其他端點的URI,開啟所有的端點訪問: *,也可以指定開啟端點訪問:如: /beans, /env
 9         include: "*"
10         # - /beans
11         # - /env
12         #配置排除其他端點的URI(排除即不開啟)
13         #exclude:
14         # - /beans
15         # - /env

 

 

  重啟SpringBoot服務,如下:

   具體訪問測試就不演示了,Actuator監控獲取的所有資料都是JSON格式的,可以自己訪問測試後通過網路工具格式化JSON。

二.視覺化UI介面實現監控應用

  Actuator來監控Spring Boot應用,其提供了許多REST介面來檢視應用的資訊。但其返回的是大量的JSON格式資料,資訊看上去不直觀也不易於理解。Spring Boot Admin(SBA)是一款基於Actuator開發的開源軟體(是一個針對spring-bootactuator介面進行UI美化封裝的監控工具),可以實現圖形化介面的方式展示Spring Boot應用的配置資訊、Beans資訊、環境屬性、執行緒資訊、JVM狀況等。官方說明文件點這裡。選擇版本文件說明,內有詳情使用說明。

  SpringBootAdmin的使用是需要建立服務端與客戶端的。Spring Boot Admin包含admin-serveradmin-client兩個元件,admin-server通過採集actuator端點資料,顯示在spring-boot-admin-ui上。

  服務端:獨立的專案,會將蒐集到的資料在自己的圖形介面中展示。依賴admin-server

  客戶端:需要監控的專案。依賴Actuator,admin-client

  對應關係:一個服務端可以監控多個客戶端。

 1.建立Spring Boot Admin Server服務端

  溫馨提示:服務端屬於一個獨立SpringBoot的專案(新建專案)。

  1. 新建SpringBoot專案(當前版本2.4.0)。
  2. 修改pom引入SpringBootAdmin-Server依賴。
    既然是一個服務端,那肯定是需要啟動容器(Tomcat)的,所以具體的依賴,你看情況新增(可以選擇根據SpringBoot新建專案時勾選依賴,也可以從Maven中查詢)
    1 <!-- spring-boot-admin-服務端依賴 -->
    2 <dependency>
    3     <groupId>de.codecentric</groupId>
    4     <artifactId>spring-boot-admin-starter-server</artifactId>
    5     <version>2.3.1</version>
    6 </dependency>
  3. 修改yml配置檔案
    畢竟服務端屬於一個獨立SpringBoot的專案,管理的客戶端也是一個獨立的專案,如果不更改埠,可能埠會出現佔用情況,所以更新配置檔案修改埠
    1 server:
    2   #servlet:
    3     # 配置訪問路徑(可選)
    4     #context-path: /admin-server
    5   # 配置埠
    6   port: 9090
  4. 啟動類添加註解@EnableAdminServer
    開啟SpringBootAdmin-Server服務端
    1 @SpringBootApplication
    2 @EnableAdminServer
    3 public class SpringbootAdminServerApplication {
  5. 啟動服務訪問
    http://localhost:9090/   效果圖如下

    因為還沒新增客戶端,所以監控列表裡是空的,接下來建立一個Spring Boot應用作為客戶端。

 1.建立Spring Boot Admin Client客戶端

   admin-server通過採集actuator端點資料,將資訊解析展示在UI介面的。那麼客戶端就需要依賴Actuator和admin-clietn。

  1. 修改pom引入admin-client依賴
    本次案例演示,直接在前面的Actuator專案中更新
    1 <!-- spring-boot-admin-starter-client客戶端依賴:注意要與服務端版本一致 -->        
    2 <dependency>
    3     <groupId>de.codecentric</groupId>
    4     <artifactId>spring-boot-admin-starter-client</artifactId>
    5     <version>${spring-boot-admin.version}</version>
    6 </dependency>

    注意:1.版本要與服務端一致。2.客戶端專案依賴不能少了Actuator。

  2. 修改yml檔案新增必須配置
    更多配置及應用參考:Spring-Boot-Admin官方文件
     1 #配置訪問端點的根路徑
     2 management: 
     3   endpoints:
     4     web:
     5       # 配置訪問端點的根路徑,預設是/actuator
     6       #base-path: /actuator
     7       exposure:
     8         #配置開啟其他端點的URI,開啟所有的端點訪問: *,也可以指定開啟端點訪問:如: /beans, /env
     9         include: "*"
    10         # - /beans
    11         # - /env
    12         #配置排除其他端點的URI(排除即不開啟)
    13         #exclude:
    14         # - /beans
    15         # - /env
    16         
    17 spring:
    18   boot:
    19     admin:
    20       client:
    21         # 配置客戶端,被監控的服務端地址(官方說明也很詳細的,這個是必須配置的)
    22         url:
    23         - http://localhost:9090
  3. 啟動客戶端專案
    之前服務端啟動後,專案監控為0,當這個客戶端啟動後,需要稍等片刻,重新請求訪問服務端地址(http://localhost:9090),即可看到如下:

     點選左上“應用牆”進入SpringBootAdmin主頁,顯示執行時間,專案個數。點選進入詳情頁,即可已圖形報表UI介面方式顯示所有監控資訊:
     如上的操作更多的可能是涉及到了測試/運維的問題,所以這裡就不深究了,但某些時候需要我們開發人員配和實現更多操作,例如郵件方式傳送檢測日誌,資訊等。詳情參考SpringBootAdmin

二,SpringBoot中的非同步呼叫

  實際開發中,有一些業務可能和實際業務無關,但又不能沒有,例如:註冊新使用者,立即送積分(沒有點選領取但積分已到賬),或下單成功,傳送push訊息等等。

  通常我們開發的程式都是同步呼叫的,即程式按照程式碼的順序一行一行的逐步往下執行,每一行程式碼都必須等待上一行程式碼執行完畢才能開始執行。而非同步程式設計則沒有這個限制,程式碼的呼叫不再是阻塞的。所以在一些情景下,通過非同步程式設計可以提高效率,提升介面的吞吐量。

  使用非同步的好處?就拿註冊使用者送積分來說:

  1. 容錯性。(不能因為積分贈送失敗,導致使用者註冊不成功!)
    註冊使用者是主要業務,積分贈送失敗,可以在提供有針對性的異常補償!
  2. 提升效能和介面吞吐量
    使用者註冊,需要20s,送積分需要10s,如果同步呼叫,則使用者需要等待30s,如果非同步呼叫,使用者只需要消耗20s即可註冊成功。

1.沒有非同步呼叫程式碼測試

  不使用非同步呼叫,即同步呼叫測試。

  1. 編寫Service提供兩個方法,一個實現註冊,一個實現送積分。
    為便於清晰測試,在積分方法中使用執行緒休眠。
     1 @Service
     2 public class AsynService {
     3     
     4     /**
     5      * 註冊方法
     6      */
     7     public void register() {
     8         System.out.println("註冊成功.......");
     9     }
    10     /**
    11      * 非同步方法送積分
    12      */
    13     public void asynIntegral() {
    14         try {
    15             Thread.sleep(3000);// 休息三秒
    16         } catch (InterruptedException e) {
    17             // TODO Auto-generated catch block
    18             e.printStackTrace();
    19         }
    20         System.out.println("送積分100分....執行者執行緒名為:"+Thread.currentThread().getName());
    21     }
    22     
    23 }
  2. 編寫Controoler註冊功能,在註冊後呼叫送積分方法。
     1 @RestController
     2 @Slf4j // 獲取log物件,Lombok外掛中的註解
     3 public class AsynController {
     4     
     5     @Autowired
     6     private AsynService asynService;
     7     
     8     @RequestMapping("/register")
     9     public String asynTest() {
    10         long startTmie = System.currentTimeMillis();
    11         // 註冊邏輯:呼叫註冊方法
    12         asynService.register();
    13         // 呼叫送積分方法
    14         asynService.asynIntegral();
    15         long endtTmie = System.currentTimeMillis();
    16         log.info("消耗時間:"+(endTime-starttTmie));
    17         return "ok";
    18     }
    19     
    20 }
  3. 頁面測試訪問,檢視控制檯執行總耗時.
    localhost:8080/register
    註冊成功.......
    送積分100分....執行者執行緒名稱為:http-nio-8080-exec-1
    2020-12-02 20:00:15.112  INFO 4604 --- [nio-8080-exec-1] c.xsge123.app.controller.AsynController  : 消耗時間:3002

    結束語:在同步呼叫時,頁面不會立刻重新整理註冊結果,只有註冊和送積分的功能全部執行完成後,頁面才會顯示註冊通知。客戶需等待較長的時間。程式總耗時較長。

2,非同步呼叫程式碼測試

  在同步程式碼基礎上,新增配置類AsyncConfig,配置開啟非同步呼叫,同時在需要使用非同步呼叫的方法上使用註解@Async即可。

  1. 新增配置類AsyncConifg,使用註解@EnableAsync開啟非同步呼叫 ,並將配置類加入到Spring容器中。
      準確的說註解@EnableAsync直接在SpringBoot啟動類上使用即可。那麼這個新增配置類的意義何在呢?SpringBoot非同步呼叫的實質是,為非同步呼叫方法開啟了新的執行緒(提供這個配置類,是為了便於後面自定義執行緒池的)當然如果不考慮自定義執行緒池,則可以直接選擇在SpringBoot啟動類上使用註解,這樣就可以省略配置類了。但程式....你細品。
    1 @Configuration // 將類注入到Spring容器中
    2 @EnableAsync //開啟SpringBoot非同步呼叫(針對非同步呼叫的方法,開啟新執行緒),在啟動類上使用也可以 
    3 public class AsyncConfig {
    4 
    5 }
  2. 在需要非同步呼叫的業務方法上使用註解@Aynsc,表示當前方法為非同步方法
     1 /**
     2  * 非同步方法送積分
     3  */
     4 @Async // 標註當前方法為非同步方法
     5 public void asynIntegral() {
     6     try {
     7         Thread.sleep(3000);// 休息3秒
     8     } catch (InterruptedException e) {
     9         // TODO Auto-generated catch block
    10         e.printStackTrace();
    11     }
    12     System.out.println("送積分100分...."+Thread.currentThread().getName());
    13 }
  3. 啟動服務測試,檢視控制檯耗時。
    localhost:8080/register
    註冊成功.......
    2020-12-02 20:18:11.764  INFO 4604 --- [nio-8080-exec-2] c.xsge123.app.controller.AsynController  : 消耗時間:6
    送積分100分....task-1
    送積分100分....score-2
    送積分100分....score-3

    結束語:在非同步呼叫的情況下,頁面即刻響應註冊結果,無需等待,後臺等待3秒後,送積分程式顯示執行結果。

3,自定義非同步呼叫執行緒池

  @Async註解,在預設情況下,用的是SimpleAsyncTaskExecutor執行緒池,該執行緒池不是真正意義上的執行緒池,因為執行緒不可重用,每次呼叫都會新建一條執行緒。可以通過控制檯日誌輸出檢視,每次列印的執行緒名都是[task -1]、[task -2]、[task-3]、[task-4].....遞增的。

  @Async註解非同步框架提供有多種執行緒池支援:
  SimpLeAsyncTaskExecutor:不是真的執行緒池,這個類不可重用執行緒,每次呼叫都會建立一個新的執行緒 。
  SyncTaskExecutor:這個類沒有實現非同步呼叫,只是一個同步操作。只適用於不需要多執行緒的地方
  ConcurrentTaskExecutor:Executor的適配類,不推薦使用。如果ThreadPoolTaskExecutor不滿足要求時,才用考慮使用這個類
  ThreadPoolTaskScheduler:可以使用cron表示式。
  ThreadPoolTaskExecutor :最常使用,推薦。 其實質是對java.util.concurrent .ThreadPoolExecutor的包裝類。

  1.  修改AsyncConfig配置類,新增自定義執行緒池方法,注入bean
     1 @Configuration // 將類注入到Spring容器中
     2 @EnableAsync //開啟SpringBoot非同步呼叫(針對非同步呼叫的方法,開啟新執行緒) 
     3 public class AsyncConfig {
     4     
     5     @Bean(name = "scorePoolTaskExecutor")
     6     public ThreadPoolTaskExecutor getScorePoolTaskExecutor() {
     7         // 建立執行緒池物件
     8         ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
     9         //核心執行緒數
    10         taskExecutor.setCorePoolSize(10);
    11         //執行緒池維護執行緒的最大數量,只有在緩衝佇列滿了之後才會申請超過核心執行緒數的執行緒
    12         taskExecutor.setMaxPoolSize(100);
    13         //快取佇列
    14         taskExecutor.setQueueCapacity(50) ;
    15         //許的空閒時間,當超過了核心執行緒出之外的執行緒在空閒時間到達之後會被銷燬
    16         taskExecutor.setKeepAliveSeconds (200) ;
    17         //.非同步方法內部執行緒名稱
    18         taskExecutor.setThreadNamePrefix("score-");
    19         /** 
    20         * 當執行緒池的任務快取佇列已滿,並且執行緒池中的執行緒數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略
    21         * 通常有以下四種策略:
    22         * ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecuti onException異常。
    23         * ThreadPoolExecutor.DiscardPolicy: 也是丟棄任務,但是不丟擲異常。
    24         * ThreadPoolExecutor. DiscardOldestPolicy: 丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程)
    25         * ThreadPoolExecutor . CallerRunsPolicy:重試添加當前的任務,自動重複呼叫execute()方法,直到成功
    26         */
    27         taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    28         taskExecutor.initialize() ;
    29         return taskExecutor;
    30     }
    31 
    32 }
  2. 修改註解@Async的使用,在使用註解時,指定使用的自定義執行緒
     1 /**
     2  * 非同步方法送積分
     3  */
     4 @Async("scorePoolTaskExecutor") // 標註當前方法為非同步方法
     5 public void asynIntegral() {
     6     try {
     7         Thread.sleep(3000);
     8     } catch (InterruptedException e) {
     9         // TODO Auto-generated catch block
    10         e.printStackTrace();
    11     }
    12     System.out.println("送積分100分...."+Thread.currentThread().getName());
    13 }
  3. 啟動服務,測試自定義執行緒池實現非同步呼叫,檢視控制檯結果
    註冊成功.......
    2020-12-02 20:45:57.898  INFO 4604 --- [nio-8080-exec-3] c.xsge123.app.controller.AsynController  : 消耗時間:2
    送積分100分....score-1
    送積分100分....score-2
    送積分100分....score-3

    結束語:執行緒實現自定義 

  

&n