Spring Boot Admin使用及心跳檢測原理
介紹
Spring Boot Admin是一個Github上的一個開源專案,它在Spring Boot Actuator的基礎上提供簡潔的視覺化WEB UI,是用來管理 Spring Boot 應用程式的一個簡單的介面,提供如下功能:
顯示 name/id 和版本號
顯示線上狀態
Logging日誌級別管理
JMX beans管理
Threads會話和執行緒管理
Trace應用請求跟蹤
-
應用執行引數資訊,如:
Java 系統屬性
Java 環境變數屬性
記憶體資訊
Spring 環境屬性
Spring Boot Admin 包含服務端和客戶端,按照以下配置可讓Spring Boot Admin執行起來。
本文示例程式碼:
使用
Server端
1、pom檔案引入相關的jar包
新建一個admin-server的Spring Boot專案,在pom檔案中引入server相關的jar包
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-server</artifactId> <version>1.5.3</version> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-server-ui</artifactId> <version>1.5.3</version> </dependency> <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>1.5.3</version> </dependency>
其中spring-boot-admin-starter-client
的引入是讓server本身能夠發現自己(自己也是客戶端)。
2、 application.yml配置
在application.yml配置如下,除了server.port=8083
的配置是server 對外公佈的服務埠外,其他配置是server本身作為客戶端的配置,包括指明指向服務端的地址和當前應用的基本資訊,使用@@可以讀取pom.xml
的相關配置。
在下面Client配置的講解中,可以看到下面類似的配置。
server: port: 8083spring: boot: admin: url: name: server description: @project.description@ version: @project.version@
3、配置日誌級別
在application.yml的同級目錄,新增檔案logback.xml
,用以配置日誌的級別,包含的內容如下:
<?xml version="1.0" encoding="UTF-8"?><configuration> <include resource="org/springframework/boot/logging/logback/base.xml"/> <logger name="org.springframework.web" level="DEBUG"/> <jmxConfigurator/></configuration>
在此處配置成了DEBUG
,這樣可以透過控制檯日誌檢視server端和client端的互動情況。
4、新增入口方法註解
在入口方法上新增@EnableAdminServer
註解。
@Configuration@EnableAutoConfiguration@EnableAdminServerpublic class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); } }
5、啟動專案
啟動admin-server專案後,可以看到當前註冊的客戶端,點選明細,還可以檢視其他明細資訊。
Spring Boot Admin Server
Client端
在上述的Server端配置中,server本身也作為一個客戶端註冊到自己,所以client配置同server端配置起來,比較見到如下。
建立一個admin-client專案,在pom.xml
新增相關client依賴包。
1、pom.xml新增client依賴
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-client</artifactId> <version>1.5.3</version> </dependency>
2、application.yml配置
在application.yml配置註冊中心地址等資訊:
spring: boot: admin: url: name: client description: @project.description@ version: @project.version@ endpoints: trace: enabled: true sensitive: false
3、配置日誌檔案
在application.yml的同級目錄,新增檔案logback.xml
,用以配置日誌的級別,包含的內容如下:
<?xml version="1.0" encoding="UTF-8"?><configuration> <include resource="org/springframework/boot/logging/logback/base.xml"/> <logger name="org.springframework.web" level="DEBUG"/> <jmxConfigurator/></configuration>
配置為DEBUG
的級別,可以輸出和伺服器的通訊資訊,以便我們在後續心跳檢測,瞭解Spring Boot Admin的實現方式。
4、啟動Admin-Client應用
啟動客戶端專案,在服務端監聽了客戶端的啟動,並在頁面給出了訊息提示,啟動後,服務端的介面顯示如下:(兩個客戶端都為UP
狀態)
Spring Boot Admin Client 啟動後
以上就可以使用Spring Boot Admin的各種監控服務了,下面談一談客戶端和服務端怎麼樣做心跳檢測的。
心跳檢測/健康檢測原理
原理
在Spring Boot Admin中,Server端作為註冊中心,它要監控所有的客戶端當前的狀態。要知道當前客戶端是否當機,剛釋出的客戶端也能夠主動註冊到服務端。
服務端和客戶端之間透過特定的介面通訊(/health介面)通訊,來監聽客戶端的狀態。因為客戶端和服務端不能保證釋出順序。
有如下的場景需要考慮:
客戶端先啟動,服務端後啟動
服務端先啟動,客戶端後啟動
服務端執行中,客戶端下線
客戶端執行中,服務端下線
所以為了解決以上問題,需要客戶端和服務端都設定一個任務監聽器,定時監聽對方的心跳,並在伺服器及時更新客戶端狀態。
上文的配置使用了客戶端主動註冊的方法。
除錯準備
為了理解Spring Boot Admin的實現方式,可透過DEBUG
和檢視日誌的方式理解伺服器和客戶端的通訊(心跳檢測)
在pom.xml右鍵spring-boot-admin-server和spring-boot-admin-starter-client,Maven->
DownLoad Sources and Documentation在logback.xml中設定日誌級別為DEBUG
客戶端發起POST請求
客戶端相關類
RegistrationApplicationListener
ApplicationRegistrator
在客戶端啟動的時候呼叫RegistrationApplicationListener的startRegisterTask,該方法每隔 registerPeriod = 10_000L,(10秒:預設)向服務端POST一次請求,告訴伺服器自身當前是有心跳
的。
RegistrationApplicationListener
@EventListener @Order(Ordered.LOWEST_PRECEDENCE) public void onApplicationReady(ApplicationReadyEvent event) { if (event.getApplicationContext() instanceof WebApplicationContext && autoRegister) { startRegisterTask(); } } public void startRegisterTask() { if (scheduledTask != null && !scheduledTask.isDone()) { return; } scheduledTask = taskScheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { registrator.register(); } }, registerPeriod); LOGGER.debug("Scheduled registration task for every {}ms", registerPeriod); }
ApplicationRegistrator
public boolean register() { boolean isRegistrationSuccessful = false; Application self = createApplication(); for (String adminUrl : admin.getAdminUrl()) { try { @SuppressWarnings("rawtypes") ResponseEntity<Map> response = template.postForEntity(adminUrl, new HttpEntity<>(self, HTTP_HEADERS), Map.class); if (response.getStatusCode().equals(HttpStatus.CREATED)) { if (registeredId.compareAndSet(null, response.getBody().get("id").toString())) { LOGGER.info("Application registered itself as {}", response.getBody()); } else { LOGGER.debug("Application refreshed itself as {}", response.getBody()); } isRegistrationSuccessful = true; if (admin.isRegisterOnce()) { break; } } else { if (unsuccessfulAttempts.get() == 0) { LOGGER.warn( "Application failed to registered itself as {}. Response: {}. Further attempts are logged on DEBUG level", self, response.toString()); } else { LOGGER.debug("Application failed to registered itself as {}. Response: {}", self, response.toString()); } } } catch (Exception ex) { if (unsuccessfulAttempts.get() == 0) { LOGGER.warn( "Failed to register application as {} at spring-boot-admin ({}): {}. Further attempts are logged on DEBUG level", self, admin.getAdminUrl(), ex.getMessage()); } else { LOGGER.debug("Failed to register application as {} at spring-boot-admin ({}): {}", self, admin.getAdminUrl(), ex.getMessage()); } } } if (!isRegistrationSuccessful) { unsuccessfulAttempts.incrementAndGet(); } else { unsuccessfulAttempts.set(0); } return isRegistrationSuccessful; }
在主要的register()方法中,向服務端POST
了Restful請求,請求的地址為/api/applications
並把自身資訊帶了過去,服務端接受請求後,透過sha-1演算法計算客戶單的唯一ID,查詢hazelcast快取資料庫,如第一次則寫入,否則更新。
服務端接收處理請求相關類
RegistryController
@RequestMapping(method = RequestMethod.POST) public ResponseEntity<Application> register(@RequestBody Application application) { Application applicationWithSource = Application.copyOf(application).withSource("http-api") .build(); LOGGER.debug("Register application {}", applicationWithSource.toString()); Application registeredApp = registry.register(applicationWithSource); return ResponseEntity.status(HttpStatus.CREATED).body(registeredApp); }
ApplicationRegistry
public Application register(Application application) { Assert.notNull(application, "Application must not be null"); Assert.hasText(application.getName(), "Name must not be null"); Assert.hasText(application.getHealthUrl(), "Health-URL must not be null"); Assert.isTrue(checkUrl(application.getHealthUrl()), "Health-URL is not valid"); Assert.isTrue( StringUtils.isEmpty(application.getManagementUrl()) || checkUrl(application.getManagementUrl()), "URL is not valid"); Assert.isTrue( StringUtils.isEmpty(application.getServiceUrl()) || checkUrl(application.getServiceUrl()), "URL is not valid"); String applicationId = generator.generateId(application); Assert.notNull(applicationId, "ID must not be null"); Application.Builder builder = Application.copyOf(application).withId(applicationId); Application existing = getApplication(applicationId); if (existing != null) { // Copy Status and Info from existing registration. builder.withStatusInfo(existing.getStatusInfo()).withInfo(existing.getInfo()); } Application registering = builder.build(); Application replaced = store.save(registering); if (replaced == null) { LOGGER.info("New Application {} registered ", registering); publisher.publishEvent(new ClientApplicationRegisteredEvent(registering)); } else { if (registering.getId().equals(replaced.getId())) { LOGGER.debug("Application {} refreshed", registering); } else { LOGGER.warn("Application {} replaced by Application {}", registering, replaced); } } return registering; }
HazelcastApplicationStore (快取資料庫)
在上述更新狀態使用了publisher.publishEvent事件訂閱的方式,接受者接收到該事件,做應用的業務處理,在這塊使用這種方式個人理解是為了程式碼的複用性,因為服務端定時輪詢客戶端也要更新客戶端在伺服器的狀態。
pulishEvent設計到的類有:
StatusUpdateApplicationListener->onClientApplicationRegistered
StatusUpdater-->updateStatus
這裡不詳細展開,下文還會提到,透過日誌,可以檢視到客戶端定時傳送的POST請求:
客戶端定時POST
服務端定時輪詢
在伺服器當機的時候,伺服器接收不到請求,此時伺服器不知道客戶端是什麼狀態,(當然可以說伺服器在一定的時間裡沒有收到客戶端的資訊,就認為客戶端掛了
,這也是一種處理方式),在Spring Boot Admin中,服務端透過定時輪詢客戶端的/health介面來對客戶端進行心態檢測。
這裡設計到主要的類為:
StatusUpdateApplicationListene
@EventListener public void onApplicationReady(ApplicationReadyEvent event) { if (event.getApplicationContext() instanceof WebApplicationContext) { startStatusUpdate(); } } public void startStatusUpdate() { if (scheduledTask != null && !scheduledTask.isDone()) { return; } scheduledTask = taskScheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { statusUpdater.updateStatusForAllApplications(); } }, updatePeriod); LOGGER.debug("Scheduled status-updater task for every {}ms", updatePeriod); }
StatusUpdater
public void updateStatusForAllApplications() { long now = System.currentTimeMillis(); for (Application application : store.findAll()) { if (now - statusLifetime > application.getStatusInfo().getTimestamp()) { updateStatus(application); } } }public void updateStatus(Application application) { StatusInfo oldStatus = application.getStatusInfo(); StatusInfo newStatus = queryStatus(application); boolean statusChanged = !newStatus.equals(oldStatus); Application.Builder builder = Application.copyOf(application).withStatusInfo(newStatus); if (statusChanged && !newStatus.isOffline() && !newStatus.isUnknown()) { builder.withInfo(queryInfo(application)); } Application newState = builder.build(); store.save(newState); if (statusChanged) { publisher.publishEvent( new ClientApplicationStatusChangedEvent(newState, oldStatus, newStatus)); } }
作者:billJiang
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2325/viewspace-2820478/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Spring Boot Admin 使用Spring Boot
- Spring Boot Admin簡介及實踐Spring Boot
- 心跳檢測
- 心跳檢測機制
- Spring Boot Admin 2.0 詳解Spring Boot
- Spring Boot Admin 2.1.0 全攻略Spring Boot
- spring-boot-route(十九)spring-boot-admin監控服務Springboot
- spring-boot-admin對spring-boot專案進行監控Springboot
- Spring Boot 原理<1>Spring Boot
- Spring Boot乾貨系列:(十二)Spring Boot使用單元測試Spring Boot
- Spring Boot Admin 2.0開箱體驗Spring Boot
- Spring Boot Admin2.1應用監控Spring Boot
- (7)Spring Boot Starter的介紹及使用Spring Boot
- Spring Boot學習筆記---Spring Boot 基礎及使用idea搭建專案Spring Boot筆記Idea
- Keepalived檢測mysql 3306心跳的指令碼MySql指令碼
- spring-boot-plus1.1.0.釋出-整合Spring Boot Admin管理和監控應用Spring Boot
- 使用 Spring Boot 和 @SpringBootTest 進行測試Spring Boot
- 使用 Spring Boot 進行單元測試Spring Boot
- Spring Boot學習之---Spring Boot與檢索 下(十六)Spring Boot
- Spring Boot Admin實現服務健康預警Spring Boot
- Spring Boot Admin 整合診斷利器 Arthas 實踐Spring Boot
- Spring Cloud實戰系列(八) - 微服務監控Spring Boot AdminCloud微服務Spring Boot
- spring boot使用@Async非同步註解,原理+原始碼Spring Boot非同步原始碼
- Spring Boot 自動配置原理Spring Boot
- 使用Spring Boot實現動態健康檢查HealthChecksSpring Boot
- Spring Boot(十二):Spring Boot 如何測試打包部署Spring Boot
- Spring系列第三講 Spring容器基本使用及原理Spring
- Spring Boot 2.0(四):使用 Docker 部署 Spring BootSpring BootDocker
- 微信域名檢測原理及防封方案
- 使用 Spring Boot 和 @WebMvcTest 測試 MVC Web ControllerSpring BootWebMVCController
- Spring Boot Admin 2 值得了解的新變化Spring Boot
- 使用Spring Security 6.1及更高版本保護Spring Boot 3應用Spring Boot
- Spring Boot核心原理-自動配置Spring Boot
- Spring Boot 自動裝配原理Spring Boot
- websocket-heartbeat-js心跳檢測庫正式釋出WebJS
- Spring Boot 使用1Spring Boot
- 使用混沌候攻擊測試Spring Boot應用Spring Boot
- Spring Boot中使用TestContainer測試快取機制Spring BootAI快取