Spring Boot Actuator 是 Spring Boot 自帶的一個功能模組,
提供了一組已經開箱即用的生產環境下常用的特性和服務,比如應用程式的健康檢查、資訊暴露、度量收集、紀錄檔記錄等。
在實際專案中,Actuator 可以幫助我們快速瞭解應用程式的執行狀態和效能瓶頸。
整合SpringBoot-Admin監控,Spring Boot Admin 就是將 Spring Boot Actuator中提供的endpoint資訊視覺化展示。
Springboot: 2.1.1.RELEASE
注意:不同的SpringBoot版本,所用到的依賴會不一致。本版本親測可用哦。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-login</artifactId>
<version>1.5.7</version>
</dependency>
@EnableAdminServer //開啟admin伺服器端
@SpringBootApplication
public class BootAdminServerApplication {
public static void main(String[] args) {
SpringApplication.run(BootAdminServerApplication.class, args);
}
}
server:
port: 8001
servlet:
context-path: /admin-server
spring:
application:
name: admin-server
啟動服務後,存取:http://localhost:8001/admin-server
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
@EnableWebSecurity
@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {
private final String adminContextPath;
public SecuritySecureConfig(AdminServerProperties adminServer) {
this.adminContextPath = adminServer.getContextPath();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(adminContextPath + "/");
http.authorizeRequests()
.antMatchers(adminContextPath + "/login",
adminContextPath + "/assets/**",
adminContextPath + "/manage/**",
adminContextPath + "/actuator/**",
adminContextPath + "/login.html"
).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler)
.and()
.logout().logoutUrl(adminContextPath + "/logout")
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
新增以下設定
spring:
security:
user:
name: admin
password: admin
啟動服務後,存取:http://localhost:8001/admin-server
輸入使用者名稱,密碼即可。
<!--加入spring-boot-admin連線端-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
如果你不想開啟安全認證請參考以下的設定。
admin-server 未開啟安全認證
server:
port: 7001
spring:
application:
name: admin-client
# 設定spring-boot-admin伺服器端的地址
boot:
admin:
client:
url: 'http://localhost:8001/admin-server'
#展示全部細節資訊
management:
endpoints:
web:
exposure:
include: '*'
#允許admin工程遠端停止本應用
endpoint:
health:
enabled: true
show-details: always
shutdown:
enabled: true
admin-server 開啟安全認證
server:
port: 7001
spring:
application:
name: admin-client
# 設定spring-boot-admin伺服器端的地址
boot:
admin:
client:
url: 'http://localhost:8001/admin-server'
username: admin
password: admin
#展示全部細節資訊
management:
endpoints:
web:
exposure:
include: '*'
#允許admin工程遠端停止本應用
endpoint:
health:
enabled: true
show-details: always
shutdown:
enabled: true
檢視Admin-Server端頁面
當我們的服務發生異常時, 可以通過郵件、微信、釘釘等傳送告警資訊。
Server 端新增以下設定類。
@Component
public class AdminNotifier extends AbstractStatusChangeNotifier {
private static final Logger log = LoggerFactory.getLogger(AdminNotifier.class);
/**
* 訊息模板
*/
private static final String template = "<<<%s>>> \n 【服務名】: %s(%s) \n 【狀態】: %s(%s) \n 【服務ip】: %s \n 【詳情】: %s";
private String titleAlarm = "系統告警";
private String titleNotice = "系統通知";
private String[] ignoreChanges = new String[]{"UNKNOWN:UP", "DOWN:UP", "OFFLINE:UP"};
public AdminNotifier(InstanceRepository repository) {
super(repository);
}
@Override
protected boolean shouldNotify(InstanceEvent event, Instance instance) {
if (!(event instanceof InstanceStatusChangedEvent)) {
return false;
} else {
InstanceStatusChangedEvent statusChange = (InstanceStatusChangedEvent) event;
String from = this.getLastStatus(event.getInstance());
String to = statusChange.getStatusInfo().getStatus();
return Arrays.binarySearch(this.ignoreChanges, from + ":" + to) < 0 && Arrays.binarySearch(this.ignoreChanges, "*:" + to) < 0 && Arrays.binarySearch(this.ignoreChanges, from + ":*") < 0;
}
}
@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
if (event instanceof InstanceStatusChangedEvent) {
log.info("Instance {} ({}) is {}", instance.getRegistration().getName(),
event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());
String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
String messageText = null;
switch (status) {
// 健康檢查沒通過
case "DOWN":
log.info("傳送 健康檢查沒通過 的通知!");
messageText = String
.format(template, titleAlarm, instance.getRegistration().getName(), event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "健康檢查沒通過通知",
instance.getRegistration().getServiceUrl(), JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
log.info(messageText);
break;
// 服務離線
case "OFFLINE":
log.info("傳送 服務離線 的通知!");
messageText = String
.format(template, titleAlarm, instance.getRegistration().getName(), event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "服務離線通知",
instance.getRegistration().getServiceUrl(), JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
log.info(messageText);
break;
//服務上線
case "UP":
log.info("傳送 服務上線 的通知!");
messageText = String
.format(template, titleNotice, instance.getRegistration().getName(), event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "服務上線通知",
instance.getRegistration().getServiceUrl(), JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
log.info(messageText);
break;
// 服務未知異常
case "UNKNOWN":
log.info("傳送 服務未知異常 的通知!");
messageText = String
.format(template, titleAlarm, instance.getRegistration().getName(), event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "服務未知異常通知",
instance.getRegistration().getServiceUrl(), JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
log.info(messageText);
break;
default:
break;
}
} else {
log.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(),
event.getType());
}
});
}
}
如下圖所示:
文章參考:
https://juejin.cn/post/7124624039921844232
https://juejin.cn/post/7141272117277884447