Spring Boot Admin 監控介紹
Spring Boot Admin是一個Web應用,用于管理和監視Spring Boot應用程序的運行狀態。
每個Spring Boot應用程序都被視為客戶端并注冊到管理服務器。
背后的數據采集是由Spring Boot Actuator端點提供。
Spring Boot Admin 是由服務端和客戶端組成
在 Spring Boot 項目中,Spring Boot Admin 作為 Server 端,其他的要被監控的應用作為 Client 端
Spring Boot Admin 設計目的
問題:在微服務生態中,由于一個項目中服務過多,導致排查問題等造成很大的困難。
目的:實時監控各個服務的健康狀態,日志及時查看,服務異常及時通知人員進行修復等。
Spring Boot Admin 實現原理
1.所有需要被監控的服務,均加上SpringBoot提供的Actuator包
2.啟動Admin Server端,作為注冊中心,監控所有客戶端當前狀態(自己也需要被注冊并且被監控)
3.啟動Admin Clinet端,第一次主動向Admin Server端提供健康信息
4.Admin Server端定時輪詢所有監控Admin Client端的節點及時獲得最新信息
5.Admin Client端如果發生異常,Admin Server端提供了郵件功能等,及時通知用戶進行修復
Spring Boot Admin 提供了哪些功能
- 顯示健康狀況
- 顯示詳細信息,例如
- JVM和內存指標
- micrometer.io指標
- 數據源指標
- 緩存指標
- 顯示內部編號
- 關注并下載日志文件
- 查看JVM系統和環境屬性
- 查看Spring Boot配置屬性
- 支持Spring Cloud的可發布/ env-和// refresh-endpoint
- 輕松的日志級別管理
- 與JMX-beans交互
- 查看線程轉儲
- 查看http跟蹤
- 查看審核事件
- 查看http端點
- 查看預定的任務
- 查看和刪除活動會話(使用spring-session)
- 查看Flyway / Liquibase數據庫遷移
- 下載heapdump
- 狀態更改通知(通過電子郵件,Slack,Hipchat等)
- 狀態更改的事件日志(非持久性)
SpringBootAdmin2.0集成eureka
創建sunny-eureka-service
這是eureka-server端,注冊中心。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
配置應用名和端口信息,以及向sunny-admin-server-service注冊的地址為http://localhost:8888,最后暴露自己的actuator的所有端口信息,具體配置如下:
#服務端口號
server:
port: 8888
spring:
application:
name: sunny-eureka-service
eureka:
instance:
#為false時,那么注冊到Eureka中的Ip地址就是本機的Ip地址
prefer-ip-address: false
#服務注冊中心實例的主機名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
client:
# 表示是否從 eureka server 中獲取注冊信息(檢索服務),默認是true
fetch-registry: false
# 表示是否將自己注冊到 eureka server(向服務注冊中心注冊自己),默認是true
register-with-eureka: false
service-url:
#服務注冊中心的配置內容,指定服務注冊中心的位置,eureka 服務器的地址(注意:地址最后面的 /eureka/ 這個是固定值)
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#2、在原先的基礎上添加security用戶名和密碼(例如:http://username:password@localhost:8000/eureka/)
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #顯示詳細信息
啟動類
@EnableEurekaServer
@SpringBootApplication
public class SpringBootApplicationEurekaServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationEurekaServer.class);
}
}
創建sunny-admin-server-service
這是SpringBootAdmin Server端
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--如果spring-boot-starter-web 排除掉tomcat,則可以引入jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- admin server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.2.1</version>
</dependency>
<!-- eureka客戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 健康監控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
management.endpoints.web.exposure.include 配置,我這里的配置暴露的所有節點進行監控
server:
port: 8889
spring:
application:
name: sunny-admin-server-service
eureka:
instance:
#服務注冊中心實例的主機名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #顯示詳細信息
啟動類
啟動類添加@EnableAdminServer注解,開啟監控
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminServer.class);
}
}
創建sunny-admin-client-service
這是SpringBootAdmin Client端
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8183
spring:
application:
name: sunny-admin-client-service
eureka:
instance:
#服務注冊中心實例的主機名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #顯示詳細信息
啟動類
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminClient {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminClient.class);
}
}
啟動三個工程,在瀏覽器上輸入localhost:8889 ,監控平臺顯示的界面如下:
Spring Boot Admin Server 可以監控的功能很多,使用起來沒有難度,下面描述下可以監測的部分內容:
- 應用運行狀態,如時間、垃圾回收次數,線程數量,內存使用走勢。
- 應用性能監測,通過選擇 JVM 或者 Tomcat 參數,查看當前數值。
- 應用環境監測,查看系統環境變量,應用配置參數,自動配置參數。
- 應用 bean 管理,查看 Spring Bean ,并且可以查看是否單例。
- 應用計劃任務,查看應用的計劃任務列表。
- 應用日志管理,動態更改日志級別,查看日志。
- 應用 JVM 管理,查看當前線程運行情況,dump 內存堆棧信息。
- 應用映射管理,查看應用接口調用方法、返回類型、處理類等信息。
SpringBootAdmin2.0集成eureka+securty認證
Web應用程序中的身份驗證和授權有多種方法,因此Spring Boot Admin不提供默認方法。默認情況下,spring-boot-admin-server-ui提供登錄頁面和注銷按鈕。我們結合 Spring Security 實現需要用戶名和密碼登錄的安全認證
創建sunny-eureka-service
這是eureka-server端,注冊中心。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
改動點:
1.默認設置security登錄賬號密碼
2.注冊中心的地址,需要加上自己設置的賬號密碼
#服務端口號
server:
port: 8888
spring:
application:
name: sunny-eureka-service
security:
user:
name: admin
password: 123456
eureka:
instance:
#為false時,那么注冊到Eureka中的Ip地址就是本機的Ip地址
prefer-ip-address: false
#服務注冊中心實例的主機名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
metadata-map:
user.name: ${spring.security.user.name}
user.password: ${spring.security.user.password}
client:
# 表示是否從 eureka server 中獲取注冊信息(檢索服務),默認是true
fetch-registry: false
# 表示是否將自己注冊到 eureka server(向服務注冊中心注冊自己),默認是true
register-with-eureka: false
service-url:
#服務注冊中心的配置內容,指定服務注冊中心的位置,eureka 服務器的地址(注意:地址最后面的 /eureka/ 這個是固定值)
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#2、在原先的基礎上添加security用戶名和密碼(例如:http://username:password@localhost:8000/eureka/)
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #顯示詳細信息
config類
新版本的spring-cloud2.0中: Spring Security默認開啟了CSRF攻擊防御
CSRF會將微服務的注冊也給過濾了,雖然不會影響注冊中心,但是其他客戶端是注冊不了的,這里配置i就是將csrf給關閉掉
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and().httpBasic()
.and()
.csrf()
.disable();
}
}
啟動類
@EnableEurekaServer
@SpringBootApplication
public class SpringBootApplicationEurekaServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationEurekaServer.class);
}
}
創建sunny-admin-server-service
這是SpringBootAdmin Server端。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--如果spring-boot-starter-web 排除掉tomcat,則可以引入jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- admin server -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- eureka客戶端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 健康監控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8889
spring:
application:
name: sunny-admin-server-service
security:
user:
name: admin
password: 123456
eureka:
instance:
#服務注冊中心實例的主機名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
metadata-map:
user.name: ${spring.security.user.name}
user.password: ${spring.security.user.password}
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #顯示詳細信息
config類
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter( "redirectTo" );
http.authorizeRequests()
.antMatchers( adminContextPath + "/assets/**" ).permitAll()
.antMatchers( adminContextPath + "/login" ).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage( adminContextPath + "/login" ).successHandler( successHandler ).and()
.logout().logoutUrl( adminContextPath + "/logout" ).and()
.httpBasic().and()
.csrf().disable();
// @formatter:on
}
}
啟動類
@EnableAdminServer
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminServer.class);
}
}
創建sunny-admin-client-service
這是SpringBootAdmin Server端。
pom文件
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8183
spring:
application:
name: sunny-admin-client-service
security:
user:
name: admin
password: 123456
eureka:
instance:
#服務注冊中心實例的主機名
hostname: localhost
health-check-url-path: /actuator/health
status-page-url-path: /actuator/info
metadata-map:
user.name: ${spring.security.user.name}
user.password: ${spring.security.user.password}
client:
serviceUrl:
defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.instance.hostname}:8888/eureka/
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: ALWAYS #顯示詳細信息
啟動類
@EnableDiscoveryClient
@SpringBootApplication
public class SpringBootApplicationMainAdminClient {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationMainAdminClient.class);
}
}
SpringBootAdmin集成郵箱服務
郵件通知
在 Spring Boot Admin 中 當注冊的應用程序狀態更改為DOWN、UNKNOWN、OFFLINE 都可以指定觸發通知,下面講解配置郵件通知。
在sunny-admin-server-service工程的pom文件中,加上email的依賴,如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
在配置文件application.yml文件中,配置收發郵件的配置:
spring:
application:
name: sunny-admin-server-service
boot:
admin:
ui:
title: sunny-admin-server-service
notify:
mail:
from: xxxx@163.com #發件人
to: xxxx@163.com,xxxx@163.com #逗號分隔的收件人列表
cc: xxxx@163.com,xxxx@163.com #逗號分隔的抄送收件人列表
enabled: true # 開啟郵箱通知
mail:
host: smtp.163.com
username: xxxx@163.com #自己的郵箱
password: xxx #授權碼
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
default-encoding: utf-8
配置后,重啟sunny-admin-server-service工程,之后若出現注冊的客戶端的狀態從 UP 變為 OFFLINE 或其他狀態,服務端就會自動將電子郵件發送到上面配置的收件地址。
注意 : 配置了郵件通知后,會出現 反復通知 service offline / up。這個問題的原因在于 查詢應用程序的狀態和信息超時,下面給出兩種解決方案:
#方法一:增加超時時間(單位:ms)
spring.boot.admin.monitor.read-timeout=20000
#方法二:關閉閉未使用或不重要的檢查點
management.health.db.enabled=false
management.health.mail.enabled=false
management.health.redis.enabled=false
management.health.mongo.enabled=false
自定義通知
可以通過添加實現Notifier接口的Spring Beans來添加您自己的通知程序,最好通過擴展 AbstractEventNotifier或AbstractStatusChangeNotifier。在sunny-admin-server-service工程中編寫一個自定義的通知器:
@Component
public class CustomNotifier extends AbstractStatusChangeNotifier {
private static final Logger LOGGER = LoggerFactory.getLogger( LoggingNotifier.class);
public CustomNotifier(InstanceRepository repository) {
super(repository);
}
@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
if (event instanceof InstanceStatusChangedEvent) {
LOGGER.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());
String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
switch (status) {
// 健康檢查沒通過
case "DOWN":
System.out.println("發送 健康檢查沒通過 的通知!");
break;
// 服務離線
case "OFFLINE":
System.out.println("發送 服務離線 的通知!");
break;
//服務上線
case "UP":
System.out.println("發送 服務上線 的通知!");
break;
// 服務未知異常
case "UNKNOWN":
System.out.println("發送 服務未知異常 的通知!");
break;
default:
break;
}
} else {
LOGGER.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(),
event.getType());
}
});
}
}
效果圖,郵件通知
我的博客即將同步至騰訊云+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=12tesopfl6i5t