手记

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分钟),第2n+1分钟(距离前一次间隔2n分钟)

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

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;
        }
    }

原文出处:https://www.cnblogs.com/langshiquan/p/9497576.html

0人推荐
随时随地看视频
慕课网APP