|
@@ -0,0 +1,100 @@
|
|
|
+package com.ruoyi.modules.monitor.notify;
|
|
|
+
|
|
|
+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.springframework.stereotype.Component;
|
|
|
+import reactor.core.publisher.Mono;
|
|
|
+
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.Executors;
|
|
|
+import java.util.concurrent.ScheduledExecutorService;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+@Component
|
|
|
+public class ServiceChangeNotifier extends AbstractStatusChangeNotifier {
|
|
|
+
|
|
|
+ private final DingTalkNotifier dingTalkNotifier;
|
|
|
+ private final InstanceRepository repository;
|
|
|
+
|
|
|
+ /* 新增字段 */
|
|
|
+ private final ConcurrentHashMap<String, Instance> buffer = new ConcurrentHashMap<>();
|
|
|
+ private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
|
|
+
|
|
|
+
|
|
|
+ public ServiceChangeNotifier(InstanceRepository repository,
|
|
|
+ DingTalkNotifier dingTalkNotifier) {
|
|
|
+ super(repository);
|
|
|
+ this.repository = repository;
|
|
|
+ this.dingTalkNotifier = dingTalkNotifier;
|
|
|
+
|
|
|
+ /* 每 5 秒聚合发送一次(可按需调整) */
|
|
|
+ scheduler.scheduleWithFixedDelay(this::flush, 5, 5, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected boolean shouldNotify(InstanceEvent event, Instance instance) {
|
|
|
+ return event instanceof InstanceStatusChangedEvent;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
|
|
|
+ /* 仅把实例放进 buffer,不做实际推送 */
|
|
|
+ buffer.put(String.valueOf(instance.getId()), instance);
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void flush() {
|
|
|
+ if (buffer.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 拿到此刻的快照,然后清空 buffer */
|
|
|
+ List<Instance> snapshot = new ArrayList<>(buffer.values());
|
|
|
+ buffer.clear();
|
|
|
+
|
|
|
+ /* 计算每个实例的 total,并排序 */
|
|
|
+ Map<Instance, Long> totalMap = new HashMap<>();
|
|
|
+ for (Instance inst : snapshot) {
|
|
|
+ long total = repository.findAll().collectList().block().size(); // 这里简单起见直接 block
|
|
|
+ totalMap.put(inst, total);
|
|
|
+ }
|
|
|
+
|
|
|
+ snapshot.sort(Comparator.comparing(totalMap::get));
|
|
|
+
|
|
|
+ /* 依次发送钉钉 */
|
|
|
+ for (Instance instance : snapshot) {
|
|
|
+ sendOne(instance);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sendOne(Instance instance) {
|
|
|
+ String serviceName = instance.getRegistration().getName();
|
|
|
+ String newStatus = instance.getStatusInfo().getStatus();
|
|
|
+ String serviceUrl = instance.getRegistration().getServiceUrl();
|
|
|
+ String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
|
|
|
+
|
|
|
+ List<Instance> all = repository.findAll().collectList().block();
|
|
|
+ long total = all.size();
|
|
|
+ long online = all.stream().filter(i -> "UP".equals(i.getStatusInfo().getStatus())).count();
|
|
|
+ long offline = total - online;
|
|
|
+
|
|
|
+ /* 根据状态决定颜色 */
|
|
|
+ String icon = "UP".equalsIgnoreCase(newStatus)
|
|
|
+ ? "<font color=\"#00ff00\">✅</font>"
|
|
|
+ : "<font color=\"#ff0000\">🚨</font>";
|
|
|
+
|
|
|
+ String markdown = "### " + icon + " 服务状态变更告警\n" +
|
|
|
+ "- **服务名**: " + serviceName + " \n" +
|
|
|
+ "- **当前状态**: " + newStatus + " \n" +
|
|
|
+ "- **地址**: " + serviceUrl + " \n" +
|
|
|
+ "- **时间**: " + time + " \n" +
|
|
|
+ "- **统计**: 总 " + total + ",在线 " + online + ",离线 " + offline;
|
|
|
+
|
|
|
+ dingTalkNotifier.sendMarkdown("服务状态变更", markdown).subscribe();
|
|
|
+ }
|
|
|
+}
|