6. 报警系统QuickAlarm之默认报警规则扩展
约 1528 字大约 5 分钟
本篇主要是扩展默认的报警规则,使其能更加友好的支持同时选择多种报警方式
扩展遵循两个原则
- 不影响原有的配置文件格式
- 简化规则解析复杂度
I. 配置文件的扩展
先看一下原有的配置文件
{
"default": {
"level": "NONE",
"autoIncEmergency": true,
"max": 30,
"min": 3,
"threshold": [
{
"level": "LOG",
"threshold": 5,
"users": [
"yihui",
"erhui"
]
}
],
"users": [
"yihui"
]
},
"NPE,SELFDEFINE": {
"level": "LOG",
"autoIncEmergency": false,
"max": 30,
"min": 0,
"threshold": [
{
"level": "SMS",
"threshold": 20,
"users": [
"345345345345",
"123123123123"
]
},
{
"level": "WEIXIN",
"threshold": 10,
"users": [
"小灰灰Blog",
"greyBlog"
]
}
],
"users": [
"yihui"
]
}
}
我们希望是能够支持多重报警方式同时选中,那么上面的配置中, threshold中只定义了一个阀值参数显然是不合适的,主要问题在于
- 单一阀值,不允许不同报警方式存在交叉
- 两个报警方式的threshold值相等时,选中的具体是哪个不可预期
所以我们的目标是将上面的参数中,新增一个指定上限的值max
{
"default": {
"level": "NONE",
"autoIncEmergency": true,
"max": 30,
"min": 3,
"threshold": [
{
"level": "LOG",
"threshold": 5,
"users": [
"yihui",
"erhui"
]
}
],
"users": [
"yihui"
]
},
"NPE,SELFDEFINE": {
"level": "LOG",
"autoIncEmergency": false,
"max": 30,
"min": 0,
"threshold": [
{
"level": "SMS",
"threshold": 20,
"users": [
"345345345345",
"123123123123"
]
},
{
"level": "WEIXIN",
"threshold": 10,
"users": [
"小灰灰Blog",
"greyBlog"
]
}
],
"users": [
"yihui"
]
},
"ZZZ": {
"level": "LOG",
"autoIncEmergency": true,
"max": 30,
"min": 3,
"threshold": [
{
"level": "SMS",
"threshold": 20,
"max": 27,
"users": [
"345345345345",
"123123123123"
]
},
{
"level": "WEIXIN",
"threshold": 10,
"users": [
"yihui",
"erhui"
]
},
{
"level": "EMAIL",
"threshold": 9,
"max": 14,
"users": [
"yihui@xxx.com",
"erhui@xxx.com"
]
}
],
"users": [
"yihui@xxx.com"
]
}
}
向上面这般改动之后,相当于每个报警方式都可以定义自己的区间,因此允许多重报警方式存在区间的交叉,计数在交叉区间即表示选中这多重方式
II. 扩展的实现支持
从配置文件的变动来看,改动很小,只是新增一个参数而已,且这个参数不是必填的,那么对应的do应该为
public class BasicAlarmThreshold {
private String level;
/**
* 启用定义的报警方式的阀值下限,
*
* 当报警计数 count >= min
* - max 非null, count < max 则选择本报警方式;
* count >= max 则不选择本报警方式
* - max 为null(即表示为定义时),
* 则max赋值为:恰好大于 min 的 {@link BasicAlarmThreshold#threshold}值
*
*/
private int threshold;
/**
* 报警上限值,注意这是包装类型,允许为null
*/
private Integer max;
private List<String> users;
}
然后顺带着,优化一把我们的映射规则,将配置规则的DO对象,映射为业务对象
主要的映射规则如下
/**
* 将配置项转换为业务DO对象, 会做一些兼容, 保证 level. min, max, users, thresholds 都不会为null
*
* @param basicAlarmConfig
* @return
*/
private static AlarmConfig parse2BizConfig(BasicAlarmConfig basicAlarmConfig) {
if (basicAlarmConfig.getUsers() == null || basicAlarmConfig.getUsers().isEmpty()) { // 如果没有填写用户, 则直接抛弃
return null;
}
AlarmConfig alarmConfig = new AlarmConfig();
// 如果配置的报警类型是异常的, 则下面会兼容一把,设置为 NONE, 避免因为配置的原因导致系统异常
alarmConfig.setExecutor(SimpleExecuteFactory.getExecute(basicAlarmConfig.getLevel()));
alarmConfig.setAutoIncEmergency(basicAlarmConfig.isAutoIncEmergency());
// 报警用户, 要求用户必须存在
alarmConfig.setUsers(basicAlarmConfig.getUsers());
// 报警上限, 如果用户没有填写,采用默认的(因为短信报警按条数要钱, 没必要一直无上限的报)
alarmConfig.setMaxLimit(basicAlarmConfig.getMax() == null ? AlarmConfig.DEFAULT_MAX_NUM : basicAlarmConfig.getMax());
// 报警下限, 如果用户没有填写, 采用默认的最小值0
alarmConfig.setMinLimit(basicAlarmConfig.getMin() == null ? AlarmConfig.DEFAULT_MIN_NUM : basicAlarmConfig.getMin());
// 获取配置中的阀值列表,并排序
List<BasicAlarmThreshold> basicAlarmThresholdList = basicAlarmConfig.getThreshold();
if(basicAlarmThresholdList == null) {
basicAlarmThresholdList = Collections.emptyList();
}
basicAlarmThresholdList.sort(Comparator.comparingInt(BasicAlarmThreshold::getThreshold));
List<AlarmThreshold> alarmThresholdList = new ArrayList<>(basicAlarmThresholdList.size() + 2);
AlarmThreshold tmpAlarmThreshold;
BasicAlarmThreshold tmpBasicAlarmThreshold;
boolean containDefaultExecute = false;
for (int i = 0; i < basicAlarmThresholdList.size(); i++) {
tmpBasicAlarmThreshold = basicAlarmThresholdList.get(i);
tmpAlarmThreshold = new AlarmThreshold();
tmpAlarmThreshold.setExecutor(SimpleExecuteFactory.getExecute(tmpBasicAlarmThreshold.getLevel()));
tmpAlarmThreshold.setUsers(tmpBasicAlarmThreshold.getUsers());
tmpAlarmThreshold.setMin(tmpBasicAlarmThreshold.getThreshold());
if (tmpBasicAlarmThreshold.getMax() == null || tmpBasicAlarmThreshold.getMax() <= tmpBasicAlarmThreshold.getThreshold()) {
if (i == basicAlarmThresholdList.size() - 1) { // 最后一个,则使用默认的上限阀值
tmpAlarmThreshold.setMax(alarmConfig.getMaxLimit());
} else {
tmpAlarmThreshold.setMax(basicAlarmThresholdList.get(i + 1).getThreshold());
}
} else {
tmpAlarmThreshold.setMax(tmpBasicAlarmThreshold.getMax());
}
if (!containDefaultExecute) {
containDefaultExecute = tmpBasicAlarmThreshold.getLevel().equals(basicAlarmConfig.getLevel());
}
alarmThresholdList.add(tmpAlarmThreshold);
}
int thresholdSize = alarmThresholdList.size();
if (thresholdSize == 0) { // 没有配置阀值列表,直接使用默认
tmpAlarmThreshold = new AlarmThreshold();
tmpAlarmThreshold.setExecutor(alarmConfig.getExecutor());
tmpAlarmThreshold.setUsers(alarmConfig.getUsers());
tmpAlarmThreshold.setMin(alarmConfig.getMinLimit());
tmpAlarmThreshold.setMax(alarmConfig.getMaxLimit());
alarmThresholdList.add(tmpAlarmThreshold);
} else if (!containDefaultExecute) { // 不包含时默认时,补全
tmpAlarmThreshold = new AlarmThreshold();
tmpAlarmThreshold.setExecutor(alarmConfig.getExecutor());
tmpAlarmThreshold.setUsers(alarmConfig.getUsers());
tmpAlarmThreshold.setMin(alarmConfig.getMinLimit());
tmpAlarmThreshold.setMax(alarmThresholdList.get(0).getMin());
alarmThresholdList.add(0, tmpAlarmThreshold);
if (alarmThresholdList.get(thresholdSize).getMax() < alarmConfig.getMaxLimit()) {
tmpAlarmThreshold = new AlarmThreshold();
tmpAlarmThreshold.setExecutor(alarmConfig.getExecutor());
tmpAlarmThreshold.setUsers(alarmConfig.getUsers());
tmpAlarmThreshold.setMin(alarmThresholdList.get(thresholdSize).getMax());
tmpAlarmThreshold.setMax(alarmConfig.getMaxLimit());
alarmThresholdList.add(tmpAlarmThreshold);
}
}
alarmConfig.setAlarmThreshold(alarmThresholdList);
return alarmConfig;
}
在映射为业务对象的逻辑中,直接保障了AlarmThreshold
列表中的顺序为最终的需求顺序,映射规则为
/**
* 如果配置的basicAlarmThresholdList列表中包含默认的报警方式
* - 则报警方式完全按照basicAlarmThresholdList的定义来
* - eg: 默认报警为 Log, min=5, max=30
* - basicAlarmThresholdList 中定义为 : { Log, min=6 }, { Email, min=8 }, { WeiXin, min=10, max=16 }, { SMS, min=14, max=26 }
* - 则转换后的 alarmThresholdList为:
* - { Log, min=6, max=8 }, { Email, min=8, max=10 }, { WeiXin, min=10, max=16 }, { SMS, min=14, max=26 }
* - count : [6, 8) Log
* - count : [8, 10) Email
* - count : [10, 16) WeiXin
* - count : [14, 26) SMS
*
* 如果不包含默认报警方式
* - 则需要补全最外层定义的Min-Max区间中的空余位
* - eg: 默认报警为 Log, min=5, max=30
* - basicAlarmThresholdList 中定义为 : { Email, min=8 }, { WeiXin, min=10, max=16 }, { SMS, min=14, max=26 }
* - 则转换后的 alarmThresholdList为:
* - { Log, min=5, max=8 }, { Email, min=8, max=10 }, { WeiXin, min=10, max=16 }, { SMS, min=14, max=26 }, { Log, min=26, max=30 }
* - count : [5, 8) Log
* - count : [8, 10) Email
* - count : [10, 16) WeiXin
* - count : [14, 26) SMS
* - count : [26, 30) Log
*
*
* 上面改造后,很容易得知,支持多重报警方式同时工作,即当技术为14,15 时,同时发起WeiXin和SMS报警
*/
相应的就可以干掉原来不太好懂的Executor选择逻辑,对应的代码为
// com.hust.hui.alarm.core.execut.AlarmExecuteSelector#getExecute
public static List<ExecuteHelper> getExecute(final AlarmConfig alarmConfig, int count) {
// 未达到报警的下限 or 超过报警的上限时
if (count < alarmConfig.getMinLimit() || count > alarmConfig.getMaxLimit()) {
return Collections.singletonList(new ExecuteHelper(SimpleExecuteFactory.getExecute(NoneExecute.NAME), alarmConfig.getUsers()));
}
// 未开启报警升级, 直接返回
if (!alarmConfig.isAutoIncEmergency()) {
return Collections.singletonList(new ExecuteHelper(alarmConfig.getExecutor(), alarmConfig.getUsers()));
}
if (count < alarmConfig.getAlarmThreshold().get(0).getMin()) {
// 未达到报警的下限
return Collections.singletonList(new ExecuteHelper(SimpleExecuteFactory.getExecute(NoneExecute.NAME), alarmConfig.getUsers()));
}
List<ExecuteHelper> list = new ArrayList<>();
for(AlarmThreshold alarmThreshold: alarmConfig.getAlarmThreshold()) {
if (alarmThreshold.getMin() <= count && count < alarmThreshold.getMax()) {
list.add(new ExecuteHelper(alarmThreshold.getExecutor(), alarmThreshold.getUsers()));
}
if(alarmThreshold.getMin() > count) {
break;
}
}
return list;
}
项目: QuickAlarm
- 项目地址: Quick-Alarm
- 博客地址: 小灰灰Blog
Loading...