springboot admin+nacos+钉钉机器人实现服务监控
nacos做为注册中心和配置中心springboot微服务集成springboot admin做服务监控钉钉机器人及时推送服务下线、上线通知摘要Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。 应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册(通过HTTP)或使用SpringClo
nacos做为注册中心和配置中心
springboot微服务
集成springboot admin做服务监控
钉钉机器人及时推送服务下线、上线通知
- 摘要
Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。 应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册(通过HTTP)或使用SpringCloud注册中心(平台使用nacos)发现。 UI是的Vue.js应用程序,展示Spring Boot Admin Client的Actuator端点上的一些监控。服务端采用Spring WebFlux + Netty的方式。Spring Boot Admin为注册的应用程序提供以下功能:
显示健康状况
显示详细信息,例如
JVM和内存指标
micrometer.io指标
数据源指标
缓存指标
显示构建信息编号
关注并下载日志文件
查看jvm system-和environment-properties
查看Spring Boot配置属性
支持Spring Cloud的postable / env-和/ refresh-endpoint
轻松的日志级管理
与JMX-beans交互
查看线程转储
查看http-traces
查看auditevents
查看http-endpoints
查看计划任务
查看和删除活动会话(使用spring-session)
查看Flyway / Liquibase数据库迁移
下载heapdump
状态变更通知(使用了钉钉报警)
状态更改的事件日志(非持久性)
- 1:首选搭建springboot admin服务
pom.xml 关键依赖如下:
<dependencies>
<!-- 服务发现(注册中心) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.0.RELEASE</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>com.netflix.archaius</groupId>
<artifactId>archaius-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- nacos配置中心 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.1.4</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>de.codecentric</groupId>-->
<!-- <artifactId>spring-boot-admin-server-ui</artifactId>-->
<!-- <version>2.1.4</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.deepblue.cloud</groupId>
<artifactId>unity-common-lang</artifactId>
<version>${unity.common.version}</version>
<exclusions>
<exclusion>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
注意:spring-boot-starter-parent为2.1.0.RELEASE版本,spring-cloud-starter-alibaba-nacos-discovery版本和其版本要一致,nacos官网有说明。
2.关键代码如下:
DingTalkNotifier.java
@Slf4j
public class DingTalkNotifier extends AbstractStatusChangeNotifier {
@Autowired
private AlarmDingTalkRobotClient alarmDingTalkRobotClient;
@Autowired
private NacosConfigService nacosConfigService;
/**
* 消息模板
*/
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 DingTalkNotifier(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 (!nacosConfigService.getIsopen()){
return;
}
String name=instance.getRegistration().getName();
String watchapplications = nacosConfigService.getWatchapplications();
Boolean flag=watchapplications.contains(instance.getRegistration().getName());
if (!flag){
return;
}
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()));
alarmDingTalkRobotClient.sendMarkdownMessage(titleAlarm, messageText, true);
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()));
alarmDingTalkRobotClient.sendMarkdownMessage(titleAlarm, messageText, true);
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()));
alarmDingTalkRobotClient.sendMarkdownMessage(titleNotice, messageText, true);
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()));
alarmDingTalkRobotClient.sendMarkdownMessage(titleAlarm, messageText, true);
break;
default:
break;
}
} else {
log.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(),
event.getType());
}
});
}
}
注意:一定要重写shouldNotify得方法,并自定义ignoreChanges,因为AbstractStatusChangeNotifier中得ignoreChanges只有"UNKNOWN:UP"得状态,如果不重写改方法,会导致服务监控不到新上线得服务,无法发送上线服务通知到钉钉机器人得问题。 其中AlarmDingTalkRobotClient类是钉钉报警自己封装得类,这里不做详细描述,大家可以根据自己业务需求,自行封装。
NacosWatch.java
@Slf4j
public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycle {
/**
* watch delay,duration to pull new service from nacos server.
*/
private long watchDelay = 30000;
private final NacosDiscoveryProperties properties;
private final TaskScheduler taskScheduler;
private final AtomicLong nacosWatchIndex;
private final AtomicBoolean running;
private ApplicationEventPublisher publisher;
private ScheduledFuture<?> watchFuture;
private Set<String> cacheServices;
private HashMap<String, EventListener> subscribeListeners;
public NacosWatch(NacosDiscoveryProperties properties) {
this(properties, getTaskScheduler());
}
public NacosWatch(NacosDiscoveryProperties properties, TaskScheduler taskScheduler) {
this.nacosWatchIndex = new AtomicLong(0L);
this.running = new AtomicBoolean(false);
this.cacheServices = new HashSet();
this.subscribeListeners = new HashMap();
this.properties = properties;
this.taskScheduler = taskScheduler;
}
private static ThreadPoolTaskScheduler getTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.initialize();
return taskScheduler;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public void stop(Runnable callback) {
this.stop();
callback.run();
}
@Override
public void start() {
if (this.running.compareAndSet(false, true)) {
this.watchFuture =
this.taskScheduler.scheduleWithFixedDelay(this::nacosServicesWatch, watchDelay);
}
}
@Override
public void stop() {
if (this.running.compareAndSet(true, false) && this.watchFuture != null) {
this.watchFuture.cancel(true);
}
}
@Override
public boolean isRunning() {
return false;
}
@Override
public int getPhase() {
return 0;
}
public void nacosServicesWatch() {
try {
boolean changed = false;
NamingService namingService = this.properties.namingServiceInstance();
ListView<String>
listView = this.properties.namingServiceInstance().getServicesOfServer(1, 2147483647);
List<String> serviceList = listView.getData();
Set<String> currentServices = new HashSet(serviceList);
currentServices.removeAll(this.cacheServices);
if (currentServices.size() > 0) {
changed = true;
}
Iterator var6;
String serviceName;
if (this.cacheServices.removeAll(new HashSet(serviceList)) && this.cacheServices.size() > 0) {
changed = true;
var6 = this.cacheServices.iterator();
while (var6.hasNext()) {
serviceName = (String) var6.next();
namingService
.unsubscribe(serviceName, (EventListener) this.subscribeListeners.get(serviceName));
this.subscribeListeners.remove(serviceName);
}
}
this.cacheServices = new HashSet(serviceList);
var6 = this.cacheServices.iterator();
while (var6.hasNext()) {
serviceName = (String) var6.next();
if (!this.subscribeListeners.containsKey(serviceName)) {
EventListener eventListener = (event) -> {
this.publisher
.publishEvent(new HeartbeatEvent(this, this.nacosWatchIndex.getAndIncrement()));
};
this.subscribeListeners.put(serviceName, eventListener);
namingService.subscribe(serviceName, eventListener);
}
}
if (changed) {
this.publisher
.publishEvent(new HeartbeatEvent(this, this.nacosWatchIndex.getAndIncrement()));
}
} catch (Exception var9) {
log.error("Error watching Nacos Service change", var9);
}
}
}
注意:就版本得nacos中可能没有监听服务上线通知,需要自己本地写一下NacosWatch类,来监听服务上线。eureka和console作为注册中心是有该实现得。
NacosWatchAutoConfiguration.java
@Configuration
public class NacosWatchAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled", matchIfMissing = true)
public NacosWatch nacosWatchDeepBlue(NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosWatch(nacosDiscoveryProperties);
}
}
AdminApplication.java
@SpringBootApplication
@EnableDiscoveryClient
@EnableAdminServer
@EnableFeignClients
public class AdminApplication {
@Bean
public DingTalkNotifier dingDingNotifier(InstanceRepository repository) {
return new DingTalkNotifier(repository);
}
/**
* 启动的时候要注意,由于我们在controller中注入了RestTemplate,所以启动的时候需要实例化该类的一个实例
*/
@Autowired
private RestTemplateBuilder builder;
/**
*使用RestTemplateBuilder来实例化RestTemplate对象,spring默认已经注入了RestTemplateBuilder实例
*/
@Bean
public RestTemplate restTemplate() {
return builder.build();
}
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
其他代码为实现钉钉机器人得相关代码,这里不做过多描述。
更多推荐
所有评论(0)