pinpoint web报警机制算法解析
背景:
见上一篇文章
我们在使用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,否则不变。
源码:[现在看看源码会比较清晰了]
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; } }