zoukankan      html  css  js  c++  java
  • pinpoint web报警机制算法解析

    背景:

      见上一篇文章 https://www.cnblogs.com/langshiquan/p/9497464.html 

      我们在使用pinpoint的报警功能的时候,发现如果持续一段时间内一直存在异常情况,但是并不是每一分钟都会接受到pinpoint的报警邮件,而是有一个时间间隔的,本文旨在分析其报警的策略算法。

    相关类:

    1)CheckResult,该类代表了报警历史,是存储在数据库当中的。

    该类与此算法有关的字段有3个,分别为detected,sequenceCount,timingCount。

    detected代表是否报警过,sequenceCount代表已经进行检查的次数,timingCount代表第几次检查应该报警。

    2)AlarmChecker,该类代表了一次检查的结果,是在内存中的临时对象。

    该类与此算法有关的字段有1个,分别为detected。

    detected代表此次是否有异常情况

    AlarmChecker的detected字段和CheckResult的detected字段含义稍有区别,请注意区分

    算法步骤:

    1.当AlarmChecker的detected字段为true 的时候(此次有异常情况),则根据isTurnToSendAlarm方法来判断此次是否应该报警:

    1)检查CheckResult报警历史中的detected来查看历史上是否报警过,如果没有,则返回true

    2)如果detected == true,则代表历史上报过警,sequenceCount和timingCount是否相差1,如果是则返回true,否则返回false

    2.更新CheckResult记录:

    1)删除原先的记录

    2)如果AlarmChecker.detected == false,则插入一条“新”记录,detected为false,sequenceCount为0,timingCount为1。

    3)如果AlarmChecker.detected == true,则插入一条“旧”记录,detected为true,sequenceCount在原先的基础上+1,timingCount如果和sequenceCount相等则在原先的基础上乘2再加1,否则不变。

    效果:

      实现了报警延迟功能,持续异常情况下,第1分钟报警,接下来,第3分钟报警(间隔2分钟),接下来,第7分钟报警(距离前一次间隔4分钟)....

    源码:[现在看看源码会比较清晰了]

    AlarmWriter

    package com.navercorp.pinpoint.web.alarm;
    
    import java.util.List;
    import java.util.Map;
    
    import org.springframework.batch.item.ItemWriter;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.navercorp.pinpoint.web.alarm.checker.AlarmChecker;
    import com.navercorp.pinpoint.web.alarm.vo.CheckerResult;
    import com.navercorp.pinpoint.web.service.AlarmService;
    
    /**
     * @author minwoo.jung
     */
    public class AlarmWriter implements ItemWriter<AlarmChecker> {
    
        @Autowired(required = false)
        private AlarmMessageSender alarmMessageSender = new EmptyMessageSender();
    
        @Autowired
        private AlarmService alarmService;
    
        @Override
        public void write(List<? extends AlarmChecker> checkers) throws Exception {
            Map<String, CheckerResult> beforeCheckerResults = alarmService.selectBeforeCheckerResults(checkers.get(0).getRule().getApplicationId());
    
            for (AlarmChecker checker : checkers) {
                CheckerResult beforeCheckerResult = beforeCheckerResults.get(checker.getRule().getCheckerName());
    
                if (beforeCheckerResult == null) {
                    beforeCheckerResult = new CheckerResult(checker.getRule().getApplicationId(), checker.getRule().getCheckerName(), false, 0, 1);
                }
    
                if (checker.isDetected()) {
                    sendAlarmMessage(beforeCheckerResult, checker);
                }
    
                alarmService.updateBeforeCheckerResult(beforeCheckerResult, checker);
            }
        }
    
        // 防止重复报警
        private void sendAlarmMessage(CheckerResult beforeCheckerResult, AlarmChecker checker) {
            if (isTurnToSendAlarm(beforeCheckerResult)) {
                if (checker.isSMSSend()) {
                    alarmMessageSender.sendSms(checker, beforeCheckerResult.getSequenceCount() + 1);
                }
                if (checker.isEmailSend()) {
                    alarmMessageSender.sendEmail(checker, beforeCheckerResult.getSequenceCount() + 1);
                }
            }
    
        }
    
        private boolean isTurnToSendAlarm(CheckerResult beforeCheckerResult) {
            // 之前没报过警就报警
            if (!beforeCheckerResult.isDetected()) {
                return true;
            }
            // 如果之前报过警,则延迟报警;检查sequenceCount和timingCount是否相差1。
            int sequenceCount = beforeCheckerResult.getSequenceCount() + 1;
    
            if (sequenceCount == beforeCheckerResult.getTimingCount()) {
                return true;
            }
    
            return false;
        }
    }
    

      alarmService.updateBeforeCheckerResult方法

    @Override
        public void updateBeforeCheckerResult(CheckerResult beforeCheckerResult, AlarmChecker checker) {
            alarmDao.deleteCheckerResult(beforeCheckerResult);
            
            if (checker.isDetected()) {
                beforeCheckerResult.setDetected(true);
                // 更新下次应该报警的时间点
                beforeCheckerResult.increseCount();
                alarmDao.insertCheckerResult(beforeCheckerResult);
            } else {
                alarmDao.insertCheckerResult(new CheckerResult(checker.getRule().getApplicationId(), checker.getRule().getCheckerName(), false, 0, 1));
            }
            
             
        }

      beforeCheckerResult.increseCount()方法

    // 延时报警,防止每分钟都报警,引起轰炸
        public void increseCount() {
            // sequenceCount为检查的次数
            ++sequenceCount;
            // timingCount代表检查次数达到timingCount则报警
            // 如果此次已经报警,则延迟下次报警的时间
            if (sequenceCount == timingCount) {
                timingCount = sequenceCount * 2 + 1;
            }
        }
  • 相关阅读:
    Spring-Context之四:Spring容器及bean的定义
    Spring-Context之三:使用XML和Groovy DSL配置Bean
    Spring-Context之二:使用Spring提供的测试框架进行测试
    Spring-Context之一:一个简单的例子
    ActiveMQ第五弹:增加ReDelivery功能
    百度云+ KeePass 网络同步你的密码
    git生成秘钥之后同步到服务器
    谷歌chrome浏览器和火狐firefox浏览器自带http抓包工具和请求模拟插件
    ltnmp
    CentOS 下安装xdebug
  • 原文地址:https://www.cnblogs.com/langshiquan/p/9497576.html
Copyright © 2011-2022 走看看