搭建 SpringBoot Admin 项目
新建项目模块
新建项目模块 fly-actuator, 添加 SpringBoot Admin Starter 自动配置依赖
xml
<!-- SpringBoot Admin Starter
包含 1.spring-boot-admin-server: Server 端
2.spring-boot-admin-server-ui: UI 界面
3.spring-boot-admin-server-cloud: 对 Spring Cloud 的接入
-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>3.1.2</version>
</dependency>启动类添加启动注解: @EnableAdminServer
微服务注册到 SpringBoot Admin Server
添加 SpringBoot Actuator 依赖
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>暴露 SpringBoot Actuator Endpoints
yaml
# 服务端点详细监控信息
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always指定 actuator 端点的访问路径
yaml
spring:
cloud:
nacos:
discovery:
metadata:
management:
context-path: ${server.servlet.context-path}/actuator项目完整代码
pom.xml 完整依赖
xml
<dependencies>
<dependency>
<groupId>com.github.itdachen.framework</groupId>
<artifactId>fly-runner</artifactId>
</dependency>
<dependency>
<groupId>com.github.itdachen.framework</groupId>
<artifactId>fly-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- SpringBoot Admin Starter
包含 1.spring-boot-admin-server :Server 端
2.spring-boot-admin-server-ui : UI 界面
3.spring-boot-admin-server-cloud :对 Spring Cloud 的接入
-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<!-- nacos discovery -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 健康检查 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>启动类
@EnableAsync
@RefreshScope
@EnableAdminServer // 启动 SpringBoot Admin
@EnableDiscoveryClient
@SpringBootApplication
@ComponentScan(basePackages = {"com.github.itdachen"})
public class ActuatorBootstrap {
public static void main(String[] args) {
SpringBootBootstrap.run(ActuatorBootstrap.class);
}
}服务状态变更通知
@Component
public class AdminActuatorEventNotifierTakeNotes extends AbstractStatusChangeNotifier {
private static final Logger logger = LoggerFactory.getLogger(AdminActuatorEventNotifierTakeNotes.class);
/**
* 消息模板
*/
private static final String TEMPLATE_INFO = "\n <<<%s>>> \n 【服务名】: %s(%s) \n 【状态】: %s(%s) \n 【服务ip】: %s \n 【详情】: %s";
/**
* 标题:系统告警
*/
private final String TITLE_ALARM = "系统告警";
/**
* 标题:系统通知
*/
private final String TITLE_NOTICE = "系统通知";
/**
* 状态转换过滤
*/
private final String[] IGNORE_CHANGES = new String[]{"UNKNOWN:UP", "REGISTERED:UP", "DOWN:UP", "OFFLINE:UP"};
private final ITakeNotifySendService takeNotesSendNotify;
public AdminActuatorEventNotifierTakeNotes(InstanceRepository repository,
ITakeNotifySendService takeNotesSendNotify) {
super(repository);
this.takeNotesSendNotify = takeNotesSendNotify;
}
/**
* 判断是否需要告警
*
* @param event
* @param instance
* @return 判断结果
*/
@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.IGNORE_CHANGES, from + ":" + to) < 0
&& Arrays.binarySearch(this.IGNORE_CHANGES, "*:" + to) < 0
&& Arrays.binarySearch(this.IGNORE_CHANGES, from + ":*") < 0;
}
}
/***
* 实现对事件的通知
* 状态:
* DOWN: 健康检查没通过;
* OFFLINE: 服务离线;
* UP: 服务上线
* UNKNOWN: 服务未知异常
* REGISTERED: 注册
* ENDPOINTS_DETECTED: 检测到端点
*
* @author 王大宸
* @date 2022/10/12 10:08
* @param event event
* @param instance instance
* @return reactor.core.publisher.Mono<java.lang.Void>
*/
@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
String messageText = "";
if (event instanceof InstanceStatusChangedEvent) {
logger.info("Instance Status Change: [{}], [{}], [{}], [{}]",
instance.getRegistration().getName(),
event.getInstance().getValue(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),
instance.getRegistration().getServiceUrl());
String serverStatus = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
JSONObject body = new JSONObject();
body.put("instanceId", event.getInstance().getValue());
body.put("appName", instance.getRegistration().getName());
body.put("appStatus", serverStatus);
body.put("notifyEvent", serverStatus);
body.put("notifyDetails", JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
body.put("serverUri", instance.getRegistration().getServiceUrl());
switch (serverStatus) {
// 健康检查没通过
case "DOWN" -> {
messageText = String.format(TEMPLATE_INFO,
TITLE_ALARM,
instance.getRegistration().getName(),
event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),
"健康检查没通过告警",
instance.getRegistration().getServiceUrl(),
JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
logger.info("【健康检查没通过告警】:{}", messageText);
body.put("notifyEvent", TITLE_ALARM);
body.put("appStatusDes", "健康检查没通过告警");
}
// 服务离线
case "OFFLINE" -> {
messageText = String.format(TEMPLATE_INFO,
TITLE_ALARM,
instance.getRegistration().getName(),
event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),
"服务离线告警",
instance.getRegistration().getServiceUrl(),
JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
logger.info("【服务离线告警】:{}", messageText);
body.put("notifyEvent", TITLE_ALARM);
body.put("appStatusDes", "服务离线告警");
}
//服务上线
case "UP" -> {
messageText = String.format(TEMPLATE_INFO,
TITLE_NOTICE,
instance.getRegistration().getName(),
event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),
"服务上线通知", instance.getRegistration().getServiceUrl(),
JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
logger.info("【服务上线通知】:{}", messageText);
body.put("notifyEvent", TITLE_NOTICE);
body.put("appStatusDes", "服务上线通知");
}
// 服务未知异常
case "UNKNOWN" -> {
messageText = String.format(TEMPLATE_INFO,
TITLE_ALARM,
instance.getRegistration().getName(),
event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),
"服务未知异常告警", instance.getRegistration().getServiceUrl(),
JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
body.put("appStatusDes", "服务未知异常告警");
body.put("notifyEvent", TITLE_ALARM);
logger.info("【服务未知异常告警】:{}", messageText);
}
case "REGISTERED" -> {
messageText = String.format(TEMPLATE_INFO,
TITLE_NOTICE,
instance.getRegistration().getName(),
event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),
"服务注册通知", instance.getRegistration().getServiceUrl(),
JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
body.put("appStatusDes", "服务注册通知");
body.put("notifyEvent", TITLE_NOTICE);
logger.info("【服务注册通知】:{}", messageText);
}
default -> {
messageText = String.format(TEMPLATE_INFO,
TITLE_NOTICE,
instance.getRegistration().getName(),
event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),
"服务其他通知",
instance.getRegistration().getServiceUrl(),
JSONObject.toJSONString(instance.getStatusInfo().getDetails()));
body.put("appStatusDes", "服务其他通知");
body.put("notifyEvent", TITLE_NOTICE);
logger.info("【服务其他通知】:{}", messageText);
}
}
body.put("notifyMsg", messageText);
body.put("notifyEventTitle", serverStatus + "【" + body.get("appStatusDes") + "】");
takeNotesSendNotify.sendNotify(body);
} else {
logger.info("Instance Info: [{}], [{}], [{}], [{}]",
instance.getRegistration().getName(),
event.getInstance().getValue(),
event.getType(),
instance.getRegistration().getServiceUrl());
}
});
}
}配置文件
bootstrap.yml
yaml
server:
port: 8005
servlet:
context-path: /${spring.application.name}
spring:
application:
name: actuator
main:
allow-circular-references: true
allow-bean-definition-overriding: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
boot:
admin:
ui:
title: FLY-CLOUD 微服务应用实例监控
brand: <img src="assets/img/icon-spring-boot-admin.svg"><span>FLY-CLOUD 微服务应用实例监控</span>
client:
instance:
metadata:
tags:
environment: local
cloud:
nacos:
username: admin
password: 123456
discovery:
enabled: true
username: nacos
password: nacos
server-addr: 127.0.0.1:8848
namespace: 07656b3c-bb69-470a-a942-6ae27b5287cb
group: FLY_GROUP
watch-delay: 3000
watch:
enabled: true
metadata:
management:
context-path: ${server.servlet.context-path}/actuator
# 服务端点详细监控信息
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always展示
启动项目, 访问: http://127.0.0.1:8005/actuator/applications
查看控制台, 打印下面信息, 说明集成成功 
剑鸣秋朔