This is an automated email from the ASF dual-hosted git repository.

gongchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 78b0a428ff [feature] support collector alarm and refactor alarm (#2693)
78b0a428ff is described below

commit 78b0a428ffd1f1c13e05bedb6ddccc62c4c9eab1
Author: kangli <69385076+pwa...@users.noreply.github.com>
AuthorDate: Sat Apr 19 13:07:41 2025 +0800

    [feature] support collector alarm and refactor alarm (#2693)
    
    Co-authored-by: Calvin <naruse_shi...@163.com>
    Co-authored-by: aias00 <rok...@163.com>
    Co-authored-by: shown <yuluo08290...@gmail.com>
    Co-authored-by: aias00 <liuhon...@apache.org>
    Co-authored-by: tomsun28 <tomsu...@outlook.com>
---
 .../alert/calculate/AlarmCacheManager.java         |  81 +++++++++++++
 .../alert/calculate/CollectorAlertHandler.java     | 129 +++++++++++++++++++++
 .../alert/calculate/PeriodicAlertCalculator.java   |  47 +++-----
 .../alert/calculate/RealTimeAlertCalculator.java   |  51 +++-----
 .../hertzbeat/alert/dao/AlertCollectorDao.java     |  23 ++--
 .../org/apache/hertzbeat/alert/util/AlertUtil.java |  30 ++---
 .../src/main/resources/alerter_en_US.properties    |   2 +
 .../src/main/resources/alerter_zh_CN.properties    |   2 +
 .../src/main/resources/alerter_zh_TW.properties    |   2 +
 .../common/support/event/MonitorDeletedEvent.java  |   2 +-
 .../apache/hertzbeat/manager/dao/CollectorDao.java |  10 +-
 .../manager/scheduler/netty/ManageServer.java      |   9 +-
 .../manager/service/impl/CollectorServiceImpl.java |   2 +-
 .../hertzbeat/manager/dao/CollectorDaoTest.java    |   8 +-
 .../manager/service/CollectorServiceTest.java      |   4 +
 15 files changed, 296 insertions(+), 106 deletions(-)

diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AlarmCacheManager.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AlarmCacheManager.java
new file mode 100644
index 0000000000..803d0305ae
--- /dev/null
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/AlarmCacheManager.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.alert.calculate;
+
+import org.apache.hertzbeat.alert.dao.SingleAlertDao;
+import org.apache.hertzbeat.alert.util.AlertUtil;
+import org.apache.hertzbeat.common.constants.CommonConstants;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * alert cache manager
+ */
+@Component
+public class AlarmCacheManager {
+
+    /**
+     * The alarm in the process is triggered
+     * key - labels fingerprint
+     */
+    private final Map<String, SingleAlert> pendingAlertMap;
+    /**
+     * The not recover alert
+     * key - labels fingerprint
+     */
+    private final Map<String, SingleAlert> firingAlertMap;
+
+    public AlarmCacheManager(SingleAlertDao singleAlertDao) {
+        this.pendingAlertMap = new ConcurrentHashMap<>(8);
+        this.firingAlertMap = new ConcurrentHashMap<>(8);
+        List<SingleAlert> singleAlerts = 
singleAlertDao.querySingleAlertsByStatus(CommonConstants.ALERT_STATUS_FIRING);
+        for (SingleAlert singleAlert : singleAlerts) {
+            String fingerprint = 
AlertUtil.calculateFingerprint(singleAlert.getLabels());
+            singleAlert.setId(null);
+            this.firingAlertMap.put(fingerprint, singleAlert);
+        }
+    }
+
+    public void putPending(String fingerPrint, SingleAlert alert) {
+        this.pendingAlertMap.put(fingerPrint, alert);
+    }
+
+    public SingleAlert getPending(String fingerPrint) {
+        return this.pendingAlertMap.get(fingerPrint);
+    }
+
+    public SingleAlert removePending(String fingerPrint) {
+        return this.pendingAlertMap.remove(fingerPrint);
+    }
+
+    public void putFiring(String fingerPrint, SingleAlert alert) {
+        this.firingAlertMap.put(fingerPrint, alert);
+    }
+
+    public SingleAlert getFiring(String fingerPrint) {
+        return this.firingAlertMap.get(fingerPrint);
+    }
+
+    public SingleAlert removeFiring(String fingerPrint) {
+        return this.firingAlertMap.remove(fingerPrint);
+    }
+}
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/CollectorAlertHandler.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/CollectorAlertHandler.java
new file mode 100644
index 0000000000..e25bbb2ba1
--- /dev/null
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/CollectorAlertHandler.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.alert.calculate;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.alert.dao.AlertCollectorDao;
+import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
+import org.apache.hertzbeat.alert.util.AlertUtil;
+import org.apache.hertzbeat.common.constants.CommonConstants;
+import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
+import org.apache.hertzbeat.common.entity.manager.Collector;
+import org.apache.hertzbeat.common.support.event.SystemConfigChangeEvent;
+import org.apache.hertzbeat.common.util.ResourceBundleUtil;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+/**
+ * handle collector alarm
+ */
+@Component
+@Slf4j
+public class CollectorAlertHandler {
+
+    private static final String KEY_COLLECTOR_NAME = "collectorName";
+    private static final String KEY_COLLECTOR_VERSION = "collectorVersion";
+    private static final String KEY_COLLECTOR_HOST = "collectorHost";
+
+    private final AlertCollectorDao alertCollectorDao;
+
+    private final AlarmCommonReduce alarmCommonReduce;
+
+    private final AlarmCacheManager alarmCacheManager;
+
+    private ResourceBundle bundle;
+
+
+    public CollectorAlertHandler(AlarmCommonReduce alarmCommonReduce, 
AlertCollectorDao alertCollectorDao,
+                                 AlarmCacheManager alarmCacheManager) {
+        this.alarmCommonReduce = alarmCommonReduce;
+        this.alertCollectorDao = alertCollectorDao;
+        this.alarmCacheManager = alarmCacheManager;
+        this.bundle = ResourceBundleUtil.getBundle("alerter");
+    }
+
+    /**
+     * handle collector online
+     *
+     * @param identity collector name
+     */
+    public void online(final String identity) {
+        Collector collector = alertCollectorDao.findCollectorByName(identity);
+        if (collector == null) {
+            return;
+        }
+        Map<String, String> fingerPrints = new HashMap<>(8);
+        fingerPrints.put(KEY_COLLECTOR_NAME, collector.getName());
+        fingerPrints.put(KEY_COLLECTOR_VERSION, collector.getVersion());
+        fingerPrints.put(KEY_COLLECTOR_HOST, collector.getIp());
+        String fingerprint = AlertUtil.calculateFingerprint(fingerPrints);
+        SingleAlert firingAlert = alarmCacheManager.getFiring(fingerprint);
+        if (firingAlert != null) {
+            firingAlert.setTriggerTimes(1);
+            firingAlert.setEndAt(System.currentTimeMillis());
+            firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
+            alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone());
+        }
+    }
+
+
+    /**
+     * handle collector offline
+     *
+     * @param identity collector name
+     */
+    public void offline(final String identity) {
+        Collector collector = alertCollectorDao.findCollectorByName(identity);
+        if (collector == null) {
+            return;
+        }
+        long currentTimeMill = System.currentTimeMillis();
+        Map<String, String> fingerPrints = new HashMap<>(8);
+        fingerPrints.put(KEY_COLLECTOR_NAME, collector.getName());
+        fingerPrints.put(KEY_COLLECTOR_VERSION, collector.getVersion());
+        fingerPrints.put(KEY_COLLECTOR_HOST, collector.getIp());
+        String fingerprint = AlertUtil.calculateFingerprint(fingerPrints);
+        SingleAlert existingAlert = alarmCacheManager.getFiring(fingerprint);
+        if (existingAlert == null) {
+            SingleAlert newAlert = SingleAlert.builder()
+                    .labels(fingerPrints)
+                    .annotations(fingerPrints)
+                    
.content(this.bundle.getString("alerter.availability.collector.offline"))
+                    .status(CommonConstants.ALERT_STATUS_FIRING)
+                    .triggerTimes(1)
+                    .startAt(currentTimeMill)
+                    .activeAt(currentTimeMill)
+                    .build();
+            alarmCacheManager.putFiring(fingerprint, newAlert);
+            alarmCommonReduce.reduceAndSendAlarm(newAlert.clone());
+        }
+
+    }
+
+
+    @EventListener(SystemConfigChangeEvent.class)
+    public void onSystemConfigChangeEvent(SystemConfigChangeEvent event) {
+        log.info("calculate alarm receive system config change event: {}.", 
event.getSource());
+        this.bundle = ResourceBundleUtil.getBundle("alerter");
+    }
+
+}
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java
index 82bbd85cf9..eb6ebf5590 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/PeriodicAlertCalculator.java
@@ -17,19 +17,17 @@
 
 package org.apache.hertzbeat.alert.calculate;
 
-import java.util.Arrays;
 import java.util.HashMap;
-import java.util.Objects;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
 import org.apache.hertzbeat.alert.service.DataSourceService;
 import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
+import org.apache.hertzbeat.alert.util.AlertUtil;
 import org.apache.hertzbeat.common.constants.CommonConstants;
 import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
 import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.stereotype.Component;
@@ -47,22 +45,13 @@ public class PeriodicAlertCalculator {
 
     private final DataSourceService dataSourceService;
     private final AlarmCommonReduce alarmCommonReduce;
-    /**
-     * The alarm in the process is triggered
-     * key - labels fingerprint
-     */
-    private final Map<String, SingleAlert> pendingAlertMap;
-    /**
-     * The not recover alert
-     * key - labels fingerprint
-     */
-    private final Map<String, SingleAlert> firingAlertMap;
-    
-    public PeriodicAlertCalculator(DataSourceService dataSourceService, 
AlarmCommonReduce alarmCommonReduce) {
+    private final AlarmCacheManager alarmCacheManager;
+
+    public PeriodicAlertCalculator(DataSourceService dataSourceService, 
AlarmCommonReduce alarmCommonReduce,
+                                   AlarmCacheManager alarmCacheManager) {
         this.dataSourceService = dataSourceService;
         this.alarmCommonReduce = alarmCommonReduce;
-        this.pendingAlertMap = new ConcurrentHashMap<>(8);
-        this.firingAlertMap = new ConcurrentHashMap<>(8);
+        this.alarmCacheManager = alarmCacheManager;
     }
     
     public void calculate(AlertDefine rule) {
@@ -122,8 +111,8 @@ public class PeriodicAlertCalculator {
 
     private void afterThresholdRuleMatch(long currentTimeMilli, Map<String, 
String> fingerPrints,
                                          Map<String, Object> fieldValueMap, 
AlertDefine define) {
-        String fingerprint = calculateFingerprint(fingerPrints);
-        SingleAlert existingAlert = pendingAlertMap.get(fingerprint);
+        String fingerprint = AlertUtil.calculateFingerprint(fingerPrints);
+        SingleAlert existingAlert = alarmCacheManager.getPending(fingerprint);
         Map<String, String> labels = new HashMap<>(8);
         fieldValueMap.putAll(define.getLabels());
         labels.putAll(fingerPrints);
@@ -144,11 +133,11 @@ public class PeriodicAlertCalculator {
             // If required trigger times is 1, set to firing status directly
             if (requiredTimes <= 1) {
                 newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
-                firingAlertMap.put(fingerprint, newAlert);
+                alarmCacheManager.putFiring(fingerprint, newAlert);
                 alarmCommonReduce.reduceAndSendAlarm(newAlert.clone());
             } else {
                 // Otherwise put into pending queue first
-                pendingAlertMap.put(fingerprint, newAlert);
+                alarmCacheManager.putPending(fingerprint, newAlert);
             }
         } else {
             // Update existing alert
@@ -158,17 +147,17 @@ public class PeriodicAlertCalculator {
             // Check if required trigger times reached
             if 
(existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && 
existingAlert.getTriggerTimes() >= requiredTimes) {
                 // Reached trigger times threshold, change to firing status
-                pendingAlertMap.remove(fingerprint);
+                alarmCacheManager.removePending(fingerprint);
                 existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
-                firingAlertMap.put(fingerprint, existingAlert);
+                alarmCacheManager.putFiring(fingerprint, existingAlert);
                 alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone());
             }
         }
     }
 
     private void handleRecoveredAlert(Map<String, String> fingerprints) {
-        String fingerprint = calculateFingerprint(fingerprints);
-        SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
+        String fingerprint = AlertUtil.calculateFingerprint(fingerprints);
+        SingleAlert firingAlert = alarmCacheManager.removeFiring(fingerprint);
         if (firingAlert != null) {
             // todo consider multi times to tig for resolved alert
             firingAlert.setTriggerTimes(1);
@@ -176,13 +165,7 @@ public class PeriodicAlertCalculator {
             firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
             alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone());
         }
-        pendingAlertMap.remove(fingerprint);
+        alarmCacheManager.removePending(fingerprint);
     }
 
-    private String calculateFingerprint(Map<String, String> fingerPrints) {
-        List<String> keyList = 
fingerPrints.keySet().stream().filter(Objects::nonNull).sorted().toList();
-        List<String> valueList = 
fingerPrints.values().stream().filter(Objects::nonNull).sorted().toList();
-        return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
-                + Arrays.hashCode(valueList.toArray(new String[0]));
-    }
 } 
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java
index ac08a63d4b..35b0334270 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/calculate/RealTimeAlertCalculator.java
@@ -18,14 +18,12 @@
 package org.apache.hertzbeat.alert.calculate;
 
 
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
@@ -38,6 +36,7 @@ import org.apache.hertzbeat.alert.dao.SingleAlertDao;
 import org.apache.hertzbeat.alert.reduce.AlarmCommonReduce;
 import org.apache.hertzbeat.alert.service.AlertDefineService;
 import org.apache.hertzbeat.alert.util.AlertTemplateUtil;
+import org.apache.hertzbeat.alert.util.AlertUtil;
 import org.apache.hertzbeat.common.constants.CommonConstants;
 import org.apache.hertzbeat.common.entity.alerter.AlertDefine;
 import org.apache.hertzbeat.common.entity.alerter.SingleAlert;
@@ -76,37 +75,20 @@ public class RealTimeAlertCalculator {
     private static final Pattern INSTANCE_PATTERN = 
Pattern.compile("equals\\(__instance__,\\s\"(\\d+)\"\\)");
     private static final Pattern METRICS_PATTERN = 
Pattern.compile("equals\\(__metrics__,\"([^\"]+)\"\\)");
 
-    /**
-     * The alarm in the process is triggered
-     * key - labels fingerprint
-     */
-    private final Map<String, SingleAlert> pendingAlertMap;
-    /**
-     * The not recover alert
-     * key - labels fingerprint
-     */
-    private final Map<String, SingleAlert> firingAlertMap;
     private final AlerterWorkerPool workerPool;
     private final CommonDataQueue dataQueue;
     private final AlertDefineService alertDefineService;
     private final AlarmCommonReduce alarmCommonReduce;
+    private final AlarmCacheManager alarmCacheManager;
 
     public RealTimeAlertCalculator(AlerterWorkerPool workerPool, 
CommonDataQueue dataQueue,
                                    AlertDefineService alertDefineService, 
SingleAlertDao singleAlertDao,
-                                   AlarmCommonReduce alarmCommonReduce) {
+                                   AlarmCommonReduce alarmCommonReduce, 
AlarmCacheManager alarmCacheManager) {
         this.workerPool = workerPool;
         this.dataQueue = dataQueue;
         this.alarmCommonReduce = alarmCommonReduce;
         this.alertDefineService = alertDefineService;
-        this.pendingAlertMap = new ConcurrentHashMap<>(8);
-        this.firingAlertMap = new ConcurrentHashMap<>(8);
-        // Initialize firing stateAlertMap
-        List<SingleAlert> singleAlerts = 
singleAlertDao.querySingleAlertsByStatus(CommonConstants.ALERT_STATUS_FIRING);
-        for (SingleAlert singleAlert : singleAlerts) {
-            String fingerprint = calculateFingerprint(singleAlert.getLabels());
-            singleAlert.setId(null);
-            firingAlertMap.put(fingerprint, singleAlert);
-        }
+        this.alarmCacheManager = alarmCacheManager;
         startCalculate();
     }
 
@@ -329,8 +311,8 @@ public class RealTimeAlertCalculator {
     }
 
     private void handleRecoveredAlert(Map<String, String> fingerprints) {
-        String fingerprint = calculateFingerprint(fingerprints);
-        SingleAlert firingAlert = firingAlertMap.remove(fingerprint);
+        String fingerprint = AlertUtil.calculateFingerprint(fingerprints);
+        SingleAlert firingAlert = alarmCacheManager.removeFiring(fingerprint);
         if (firingAlert != null) {
             // todo consider multi times to tig for resolved alert
             firingAlert.setTriggerTimes(1);
@@ -338,13 +320,13 @@ public class RealTimeAlertCalculator {
             firingAlert.setStatus(CommonConstants.ALERT_STATUS_RESOLVED);
             alarmCommonReduce.reduceAndSendAlarm(firingAlert.clone());
         }
-        pendingAlertMap.remove(fingerprint);
+        alarmCacheManager.removePending(fingerprint);
     }
 
     private void afterThresholdRuleMatch(long currentTimeMilli, Map<String, 
String> fingerPrints,
                                          Map<String, Object> fieldValueMap, 
AlertDefine define, Map<String, String> annotations) {
-        String fingerprint = calculateFingerprint(fingerPrints);
-        SingleAlert existingAlert = pendingAlertMap.get(fingerprint);
+        String fingerprint = AlertUtil.calculateFingerprint(fingerPrints);
+        SingleAlert existingAlert = alarmCacheManager.getPending(fingerprint);
         fieldValueMap.putAll(define.getLabels());
         int requiredTimes = define.getTimes() == null ? 1 : define.getTimes();
         if (existingAlert == null) {
@@ -376,11 +358,11 @@ public class RealTimeAlertCalculator {
             // If required trigger times is 1, set to firing status directly
             if (requiredTimes <= 1) {
                 newAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
-                firingAlertMap.put(fingerprint, newAlert);
+                alarmCacheManager.putFiring(fingerprint, newAlert);
                 alarmCommonReduce.reduceAndSendAlarm(newAlert.clone());
             } else {
                 // Otherwise put into pending queue first
-                pendingAlertMap.put(fingerprint, newAlert);
+                alarmCacheManager.putPending(fingerprint, newAlert);
             }
         } else {
             // Update existing alert
@@ -390,9 +372,9 @@ public class RealTimeAlertCalculator {
             // Check if required trigger times reached
             if 
(existingAlert.getStatus().equals(CommonConstants.ALERT_STATUS_PENDING) && 
existingAlert.getTriggerTimes() >= requiredTimes) {
                 // Reached trigger times threshold, change to firing status
-                pendingAlertMap.remove(fingerprint);
+                alarmCacheManager.removePending(fingerprint);
                 existingAlert.setStatus(CommonConstants.ALERT_STATUS_FIRING);
-                firingAlertMap.put(fingerprint, existingAlert);
+                alarmCacheManager.putFiring(fingerprint, existingAlert);
                 alarmCommonReduce.reduceAndSendAlarm(existingAlert.clone());
             }
         }
@@ -426,13 +408,6 @@ public class RealTimeAlertCalculator {
         }
         return match != null && match;
     }
-    
-    private String calculateFingerprint(Map<String, String> fingerPrints) {
-        List<String> keyList = 
fingerPrints.keySet().stream().filter(Objects::nonNull).sorted().toList();
-        List<String> valueList = 
fingerPrints.values().stream().filter(Objects::nonNull).sorted().toList();
-        return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
-                + Arrays.hashCode(valueList.toArray(new String[0]));
-    }
 
     private Set<String> kvLabelsToKvStringSet(Map<String, String> labels) {
         if (labels == null || labels.isEmpty()) {
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/dao/CollectorDao.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dao/AlertCollectorDao.java
similarity index 66%
copy from 
hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/dao/CollectorDao.java
copy to 
hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dao/AlertCollectorDao.java
index 4aefaa2e79..c76a06e6dc 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/dao/CollectorDao.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/dao/AlertCollectorDao.java
@@ -15,30 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.hertzbeat.manager.dao;
+package org.apache.hertzbeat.alert.dao;
 
-import java.util.Optional;
 import org.apache.hertzbeat.common.entity.manager.Collector;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.data.jpa.repository.Modifying;
 
 /**
- * Collector repository
+ * Alert Collector Dao
  */
-public interface CollectorDao extends JpaRepository<Collector, Long>, 
JpaSpecificationExecutor<Collector> {
-    
+public interface AlertCollectorDao extends JpaRepository<Collector, Long>, 
JpaSpecificationExecutor<Collector> {
+
     /**
-     * find collector by name
-     * @param name name
+     * Query collector by name
+     * @param name collector name
      * @return collector
      */
-    Optional<Collector> findCollectorByName(String name);
-    
-    /**
-     * delete collector by name
-     * @param collector collector name
-     */
-    @Modifying
-    void deleteCollectorByName(String collector);
+    Collector findCollectorByName(String name);
 }
diff --git 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/MonitorDeletedEvent.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertUtil.java
similarity index 55%
copy from 
hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/MonitorDeletedEvent.java
copy to 
hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertUtil.java
index 5049a46b5d..a1ed180c3f 100644
--- 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/MonitorDeletedEvent.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/util/AlertUtil.java
@@ -15,26 +15,26 @@
  * limitations under the License.
  */
 
-package org.apache.hertzbeat.common.support.event;
+package org.apache.hertzbeat.alert.util;
 
-import org.springframework.context.ApplicationEvent;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 /**
- * the event for system config change
+ * alert util
  */
-public class MonitorDeletedEvent extends ApplicationEvent {
-    
+public class AlertUtil {
+
     /**
-     * monitoring id
+     * calculate fingerprint
+     * @param fingerPrints finger prints
      */
-    private final Long monitorId;
-    
-    public MonitorDeletedEvent(Object source, Long monitorId) {
-        super(source);
-        this.monitorId = monitorId;
-    }
-    
-    public Long getMonitorId() {
-        return monitorId;
+    public static String calculateFingerprint(Map<String, String> 
fingerPrints) {
+        List<String> keyList = 
fingerPrints.keySet().stream().filter(Objects::nonNull).sorted().toList();
+        List<String> valueList = 
fingerPrints.values().stream().filter(Objects::nonNull).sorted().toList();
+        return Arrays.hashCode(keyList.toArray(new String[0])) + "-"
+                + Arrays.hashCode(valueList.toArray(new String[0]));
     }
 }
diff --git a/hertzbeat-alerter/src/main/resources/alerter_en_US.properties 
b/hertzbeat-alerter/src/main/resources/alerter_en_US.properties
index 938da98ac3..e05324b2d0 100644
--- a/hertzbeat-alerter/src/main/resources/alerter_en_US.properties
+++ b/hertzbeat-alerter/src/main/resources/alerter_en_US.properties
@@ -14,6 +14,8 @@
 # limitations under the License.
 
 alerter.availability.recover = Availability Alert Resolved, Monitor Status 
Normal Now
+alerter.availability.collector.recover = Collector Availability Alert 
Resolved, The collector is online
+alerter.availability.collector.offline = Collector Availability Alert Notify, 
The collector is offline
 alerter.alarm.recover = Alert Resolved Notice
 alerter.notify.title = HertzBeat Alert Notify
 alerter.notify.target = Monitor Target
diff --git a/hertzbeat-alerter/src/main/resources/alerter_zh_CN.properties 
b/hertzbeat-alerter/src/main/resources/alerter_zh_CN.properties
index b1f11e284d..33f4e88d52 100644
--- a/hertzbeat-alerter/src/main/resources/alerter_zh_CN.properties
+++ b/hertzbeat-alerter/src/main/resources/alerter_zh_CN.properties
@@ -14,6 +14,8 @@
 # limitations under the License.
 
 alerter.availability.recover = 可用性告警恢复通知, 任务状态已恢复正常
+alerter.availability.collector.recover = 采集器可用性恢复通知,采集器已上线
+alerter.availability.collector.offline = 采集器可用性告警通知,采集器已下线
 alerter.alarm.recover = 告警恢复通知
 alerter.notify.title = HertzBeat告警通知
 alerter.notify.target = 告警目标对象
diff --git a/hertzbeat-alerter/src/main/resources/alerter_zh_TW.properties 
b/hertzbeat-alerter/src/main/resources/alerter_zh_TW.properties
index a038fc9ec8..bc9331430b 100644
--- a/hertzbeat-alerter/src/main/resources/alerter_zh_TW.properties
+++ b/hertzbeat-alerter/src/main/resources/alerter_zh_TW.properties
@@ -15,6 +15,8 @@
 
 alerter.availability.recover = 可用性警報已解決,監視狀態現在正常
 alerter.alarm.recover = 警報解決通知
+alerter.availability.collector.recover = 采集器可用性恢復通知,采集器已上線
+alerter.availability.collector.offline = 采集器可用性告警通知,采集器已下線
 alerter.notify.title = HertzBeat 警報通知
 alerter.notify.target = 監視目標
 alerter.notify.monitorId = 監視 ID
diff --git 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/MonitorDeletedEvent.java
 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/MonitorDeletedEvent.java
index 5049a46b5d..a451aa91ba 100644
--- 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/MonitorDeletedEvent.java
+++ 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/MonitorDeletedEvent.java
@@ -20,7 +20,7 @@ package org.apache.hertzbeat.common.support.event;
 import org.springframework.context.ApplicationEvent;
 
 /**
- * the event for system config change
+ * the event for monitor delete
  */
 public class MonitorDeletedEvent extends ApplicationEvent {
     
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/dao/CollectorDao.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/dao/CollectorDao.java
index 4aefaa2e79..8affacd3ae 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/dao/CollectorDao.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/dao/CollectorDao.java
@@ -17,6 +17,7 @@
 
 package org.apache.hertzbeat.manager.dao;
 
+import java.util.List;
 import java.util.Optional;
 import org.apache.hertzbeat.common.entity.manager.Collector;
 import org.springframework.data.jpa.repository.JpaRepository;
@@ -34,7 +35,14 @@ public interface CollectorDao extends 
JpaRepository<Collector, Long>, JpaSpecifi
      * @return collector
      */
     Optional<Collector> findCollectorByName(String name);
-    
+
+    /**
+     * find collectors by names
+     * @param names collector name list
+     * @return collector list
+     */
+    List<Collector> findCollectorsByNameIn(List<String> names);
+
     /**
      * delete collector by name
      * @param collector collector name
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/scheduler/netty/ManageServer.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/scheduler/netty/ManageServer.java
index 68513fd5c5..352415b65f 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/scheduler/netty/ManageServer.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/scheduler/netty/ManageServer.java
@@ -24,6 +24,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.alert.calculate.CollectorAlertHandler;
 import org.apache.hertzbeat.common.entity.message.ClusterMsg;
 import org.apache.hertzbeat.common.support.CommonThreadPool;
 import org.apache.hertzbeat.manager.scheduler.CollectorJobScheduler;
@@ -56,6 +57,8 @@ public class ManageServer implements CommandLineRunner {
 
     private final CollectorJobScheduler collectorJobScheduler;
 
+    private final CollectorAlertHandler collectorAlertHandler;
+
     private ScheduledExecutorService channelSchedule;
 
     private RemotingServer remotingServer;
@@ -64,9 +67,11 @@ public class ManageServer implements CommandLineRunner {
 
     public ManageServer(final SchedulerProperties schedulerProperties,
                         final CollectorJobScheduler collectorJobScheduler,
-                        final CommonThreadPool threadPool) {
+                        final CommonThreadPool threadPool,
+                        final CollectorAlertHandler collectorAlertHandler) {
         this.collectorJobScheduler = collectorJobScheduler;
         this.collectorJobScheduler.setManageServer(this);
+        this.collectorAlertHandler = collectorAlertHandler;
         this.init(schedulerProperties, threadPool);
     }
 
@@ -98,6 +103,7 @@ public class ManageServer implements CommandLineRunner {
                         channel.closeFuture();
                         this.clientChannelTable.remove(collector);
                         
this.collectorJobScheduler.collectorGoOffline(collector);
+                        this.collectorAlertHandler.offline(collector);
                     }
                 });   
             } catch (Exception e) {
@@ -131,6 +137,7 @@ public class ManageServer implements CommandLineRunner {
             preChannel.close();
         }
         this.clientChannelTable.put(identity, channel);
+        this.collectorAlertHandler.online(identity);
     }
 
     public void closeChannel(final String identity) {
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/CollectorServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/CollectorServiceImpl.java
index 4be9c982b3..cafa02f291 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/CollectorServiceImpl.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/CollectorServiceImpl.java
@@ -61,7 +61,7 @@ public class CollectorServiceImpl implements CollectorService 
{
     
     @Autowired(required = false)
     private ManageServer manageServer;
-    
+
     @Override
     @Transactional(readOnly = true)
     public Page<CollectorSummary> getCollectors(String name, int pageIndex, 
Integer pageSize) {
diff --git 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/dao/CollectorDaoTest.java
 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/dao/CollectorDaoTest.java
index fab0126250..997ebeaa56 100644
--- 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/dao/CollectorDaoTest.java
+++ 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/dao/CollectorDaoTest.java
@@ -19,6 +19,7 @@ package org.apache.hertzbeat.manager.dao;
 
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import javax.annotation.Resource;
 import org.apache.hertzbeat.common.entity.manager.Collector;
 import org.apache.hertzbeat.manager.AbstractSpringIntegrationTest;
@@ -27,6 +28,8 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.transaction.annotation.Transactional;
 
+import java.util.List;
+
 /**
  * Test case for {@link CollectorDao}
  */
@@ -66,5 +69,8 @@ public class CollectorDaoTest extends 
AbstractSpringIntegrationTest {
         assertTrue(collectorDao.findCollectorByName("test").isPresent());
     }
 
-
+    @Test
+    public void findCollectorsByNameIn(){
+        
assertFalse(collectorDao.findCollectorsByNameIn(List.of("test")).isEmpty());
+    }
 }
diff --git 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/CollectorServiceTest.java
 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/CollectorServiceTest.java
index c31ec4c8b6..f5dc2095b7 100644
--- 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/CollectorServiceTest.java
+++ 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/CollectorServiceTest.java
@@ -41,6 +41,7 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.context.ApplicationContext;
 import org.springframework.data.domain.Page;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.jpa.domain.Specification;
@@ -67,6 +68,9 @@ public class CollectorServiceTest {
     @Mock
     private ManageServer manageServer;
 
+    @Mock
+    private ApplicationContext applicationContext;
+
 
     @Test
     public void getCollectors() {


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@hertzbeat.apache.org
For additional commands, e-mail: notifications-h...@hertzbeat.apache.org


Reply via email to