|
|
@@ -0,0 +1,153 @@
|
|
|
+package com.ruoyi.modules.monitor.listener;
|
|
|
+
|
|
|
+import de.codecentric.boot.admin.server.domain.entities.Instance;
|
|
|
+import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
|
|
|
+import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
|
|
|
+import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
|
|
|
+import de.codecentric.boot.admin.server.notify.AbstractStatusChangeNotifier;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import com.ruoyi.modules.monitor.service.AlarmEmailService;
|
|
|
+import reactor.core.publisher.Mono;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 服务状态变更通知器
|
|
|
+ * 通过继承AbstractStatusChangeNotifier实现自定义通知
|
|
|
+ *
|
|
|
+ * @author lydgt
|
|
|
+ */
|
|
|
+@Component
|
|
|
+public class ServiceStatusListener extends AbstractStatusChangeNotifier
|
|
|
+{
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(ServiceStatusListener.class);
|
|
|
+
|
|
|
+ private final AlarmEmailService alarmEmailService;
|
|
|
+
|
|
|
+ public ServiceStatusListener(InstanceRepository repository, AlarmEmailService alarmEmailService)
|
|
|
+ {
|
|
|
+ super(repository);
|
|
|
+ this.alarmEmailService = alarmEmailService;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理实例状态变更事件
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ protected Mono<Void> doNotify(InstanceEvent event, Instance instance)
|
|
|
+ {
|
|
|
+ return Mono.fromRunnable(() -> {
|
|
|
+ if (event instanceof InstanceStatusChangedEvent)
|
|
|
+ {
|
|
|
+ String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
|
|
|
+ String serviceName = instance.getRegistration().getName();
|
|
|
+ String serviceUrl = instance.getRegistration().getServiceUrl();
|
|
|
+
|
|
|
+ log.info("服务状态变更 - 服务: {}, URL: {}, 状态: {}", serviceName, serviceUrl, status);
|
|
|
+
|
|
|
+ switch (status)
|
|
|
+ {
|
|
|
+ // 健康检查没通过
|
|
|
+ case "DOWN":
|
|
|
+ log.warn("服务异常 - 服务: {}, 状态: DOWN", serviceName);
|
|
|
+ sendAlarmEmail(serviceName, serviceUrl, status, "DOWN", event);
|
|
|
+ break;
|
|
|
+ // 服务离线
|
|
|
+ case "OFFLINE":
|
|
|
+ log.warn("服务离线 - 服务: {}, 状态: OFFLINE", serviceName);
|
|
|
+ sendAlarmEmail(serviceName, serviceUrl, status, "OFFLINE", event);
|
|
|
+ break;
|
|
|
+ // 服务上线
|
|
|
+ case "UP":
|
|
|
+ log.info("服务恢复 - 服务: {}, 状态: UP", serviceName);
|
|
|
+ sendRecoveryEmail(serviceName, serviceUrl, status);
|
|
|
+ break;
|
|
|
+ // 服务未知异常
|
|
|
+ case "UNKNOWN":
|
|
|
+ log.warn("服务未知异常 - 服务: {}, 状态: UNKNOWN", serviceName);
|
|
|
+ sendAlarmEmail(serviceName, serviceUrl, status, "UNKNOWN", event);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log.debug("服务状态变更 - 服务: {}, 状态: {}", serviceName, status);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送告警邮件
|
|
|
+ */
|
|
|
+ private void sendAlarmEmail(String serviceName, String serviceUrl, String status, String type, InstanceEvent event)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ String subject = "【服务告警】服务异常通知 - " + serviceName;
|
|
|
+ String content = buildAlarmContent(serviceName, serviceUrl, status, type, event);
|
|
|
+ alarmEmailService.sendAlarmEmail(subject, content);
|
|
|
+ log.warn("已发送服务告警邮件 - 服务: {}, 类型: {}", serviceName, type);
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ log.error("发送告警邮件失败 - 服务: {}", serviceName, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送恢复邮件
|
|
|
+ */
|
|
|
+ private void sendRecoveryEmail(String serviceName, String serviceUrl, String status)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ String subject = "【服务恢复】服务恢复正常通知 - " + serviceName;
|
|
|
+ String content = buildRecoveryContent(serviceName, serviceUrl, status);
|
|
|
+ alarmEmailService.sendAlarmEmail(subject, content);
|
|
|
+ log.info("已发送服务恢复邮件 - 服务: {}", serviceName);
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ log.error("发送恢复邮件失败 - 服务: {}", serviceName, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建告警邮件内容
|
|
|
+ */
|
|
|
+ private String buildAlarmContent(String serviceName, String serviceUrl, String status, String type, InstanceEvent event)
|
|
|
+ {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("<h2 style='color: red;'>服务异常告警</h2>");
|
|
|
+ sb.append("<p><strong>服务名称:</strong>").append(serviceName).append("</p>");
|
|
|
+ sb.append("<p><strong>服务地址:</strong>").append(serviceUrl).append("</p>");
|
|
|
+ sb.append("<p><strong>当前状态:</strong><span style='color: red;'>").append(status).append("</span></p>");
|
|
|
+ sb.append("<p><strong>告警类型:</strong>").append(type).append("</p>");
|
|
|
+ sb.append("<p><strong>告警时间:</strong>").append(java.time.LocalDateTime.now()).append("</p>");
|
|
|
+
|
|
|
+ if (event instanceof InstanceStatusChangedEvent) {
|
|
|
+ Object details = ((InstanceStatusChangedEvent) event).getStatusInfo().getDetails();
|
|
|
+ if (details != null && !details.toString().isEmpty()) {
|
|
|
+ sb.append("<p><strong>详细信息:</strong></p><pre>").append(details.toString()).append("</pre>");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ sb.append("<hr><p style='color: gray;'>此邮件由监控系统自动发送,请勿回复。</p>");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建恢复邮件内容
|
|
|
+ */
|
|
|
+ private String buildRecoveryContent(String serviceName, String serviceUrl, String status)
|
|
|
+ {
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("<h2 style='color: green;'>服务恢复正常</h2>");
|
|
|
+ sb.append("<p><strong>服务名称:</strong>").append(serviceName).append("</p>");
|
|
|
+ sb.append("<p><strong>服务地址:</strong>").append(serviceUrl).append("</p>");
|
|
|
+ sb.append("<p><strong>当前状态:</strong><span style='color: green;'>").append(status).append("</span></p>");
|
|
|
+ sb.append("<p><strong>恢复时间:</strong>").append(java.time.LocalDateTime.now()).append("</p>");
|
|
|
+ sb.append("<hr><p style='color: gray;'>此邮件由监控系统自动发送,请勿回复。</p>");
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+}
|