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