SpringBoot第十二集:度量指標監控與非同步呼叫(2020最新最易懂)
SpringBoot第十二集:度量指標監控與非同步呼叫(2020最新最易懂)
Spring Boot Actuator
是spring 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屬性代表 了屬性的配置字首 |
GET | /beans |
描述應用程式上下文裡全部的Bean,以及它們的關係 ●bean: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屬性標識了 該對映關係的請求處理器 |
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-boot
的actuator
介面進行UI美化封裝的監控工具),可以實現圖形化介面的方式展示Spring Boot應用的配置資訊、Beans資訊、環境屬性、執行緒資訊、JVM狀況等。官方說明文件點這裡。選擇版本文件說明,內有詳情使用說明。
SpringBootAdmin的使用是需要建立服務端與客戶端的。Spring Boot Admin
包含admin-server
與admin-client
兩個元件,admin-server
通過採集actuator
端點資料,顯示在spring-boot-admin-ui
上。
服務端:獨立的專案,會將蒐集到的資料在自己的圖形介面中展示。依賴admin-server
客戶端:需要監控的專案。依賴Actuator,admin-client
對應關係:一個服務端可以監控多個客戶端。
1.建立Spring Boot Admin Server服務端
溫馨提示:服務端屬於一個獨立SpringBoot的專案(新建專案)。
- 新建SpringBoot專案(當前版本2.4.0)。
- 修改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>
- 修改yml配置檔案
畢竟服務端屬於一個獨立SpringBoot的專案,管理的客戶端也是一個獨立的專案,如果不更改埠,可能埠會出現佔用情況,所以更新配置檔案修改埠
1 server: 2 #servlet: 3 # 配置訪問路徑(可選) 4 #context-path: /admin-server 5 # 配置埠 6 port: 9090
- 啟動類添加註解@EnableAdminServer
開啟SpringBootAdmin-Server服務端
1 @SpringBootApplication 2 @EnableAdminServer 3 public class SpringbootAdminServerApplication {
- 啟動服務訪問
http://localhost:9090/ 效果圖如下
因為還沒新增客戶端,所以監控列表裡是空的,接下來建立一個Spring Boot應用作為客戶端。
1.建立Spring Boot Admin Client客戶端
admin-server
通過採集actuator
端點資料,將資訊解析展示在UI介面的。那麼客戶端就需要依賴Actuator和admin-clietn。
- 修改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。
- 修改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
- 啟動客戶端專案
之前服務端啟動後,專案監控為0,當這個客戶端啟動後,需要稍等片刻,重新請求訪問服務端地址(http://localhost:9090),即可看到如下:
點選左上“應用牆”進入SpringBootAdmin主頁,顯示執行時間,專案個數。點選進入詳情頁,即可已圖形報表UI介面方式顯示所有監控資訊:
如上的操作更多的可能是涉及到了測試/運維的問題,所以這裡就不深究了,但某些時候需要我們開發人員配和實現更多操作,例如郵件方式傳送檢測日誌,資訊等。詳情參考SpringBootAdmin
二,SpringBoot中的非同步呼叫
實際開發中,有一些業務可能和實際業務無關,但又不能沒有,例如:註冊新使用者,立即送積分(沒有點選領取但積分已到賬),或下單成功,傳送push訊息等等。
通常我們開發的程式都是同步呼叫的,即程式按照程式碼的順序一行一行的逐步往下執行,每一行程式碼都必須等待上一行程式碼執行完畢才能開始執行。而非同步程式設計則沒有這個限制,程式碼的呼叫不再是阻塞的。所以在一些情景下,通過非同步程式設計可以提高效率,提升介面的吞吐量。
使用非同步的好處?就拿註冊使用者送積分來說:
- 容錯性。(不能因為積分贈送失敗,導致使用者註冊不成功!)
註冊使用者是主要業務,積分贈送失敗,可以在提供有針對性的異常補償! - 提升效能和介面吞吐量
使用者註冊,需要20s,送積分需要10s,如果同步呼叫,則使用者需要等待30s,如果非同步呼叫,使用者只需要消耗20s即可註冊成功。
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 }
- 編寫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 }
- 頁面測試訪問,檢視控制檯執行總耗時.
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即可。
- 新增配置類AsyncConifg,使用註解@EnableAsync開啟非同步呼叫 ,並將配置類加入到Spring容器中。
準確的說註解@EnableAsync直接在SpringBoot啟動類上使用即可。那麼這個新增配置類的意義何在呢?SpringBoot非同步呼叫的實質是,為非同步呼叫方法開啟了新的執行緒(提供這個配置類,是為了便於後面自定義執行緒池的)當然如果不考慮自定義執行緒池,則可以直接選擇在SpringBoot啟動類上使用註解,這樣就可以省略配置類了。但程式....你細品。
1 @Configuration // 將類注入到Spring容器中 2 @EnableAsync //開啟SpringBoot非同步呼叫(針對非同步呼叫的方法,開啟新執行緒),在啟動類上使用也可以 3 public class AsyncConfig { 4 5 }
- 在需要非同步呼叫的業務方法上使用註解@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 }
- 啟動服務測試,檢視控制檯耗時。
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的包裝類。
- 修改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 }
- 修改註解@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 }
- 啟動服務,測試自定義執行緒池實現非同步呼叫,檢視控制檯結果
註冊成功....... 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