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

 

posted @ 2018-08-19 19:42  情猿  阅读(2634)  评论(1编辑  收藏  举报