4. 报警系统QuickAlarm之报警规则解析

文章目录
  1. I. 背景知识点
    1. 0. 声明
    2. 1. 报警规则
  2. II. 报警规则解析
    1. 1. 实现方案说明
    2. 2. 实现
  3. III. 小结
    1. 1. 报警执行器
    2. 2. 报警规则
  4. IV. 其他
    1. 相关博文
    2. 项目: QuickAlarm
    3. 个人博客: Z+|blog
    4. 声明
    5. 扫描关注

前面两篇分别说了报警执行器和报警规则的定义及用户扩展加载,接下来就是比较核心的一块了,如何将报警规则和报警执行器关联起来,即当发生报警时,应该call哪一个报警执行器

I. 背景知识点

0. 声明

在正式进入之前,有必要额外声明一下,因为目前的v1版本,没有开放报警规则的自定义,也就是说,目前只支持默认的报警规则,所以接下来的主要内容将集中在

  • 系统默认的报警规则的解析
  • 即基于报警频率阀值,自动选择报警执行器的规则解析

1. 报警规则

如果对于报警规则,依然不是很清晰的,可以阅读一下《报警系统QuickAlarm之报警规则的设定与加载》

这里简单的进行说明,系统中默认的报警规则结构为:

  • key为报警类型(即用户执行报警时,传进来的报警类型参数)
  • value为具体报警规则
    • 每个报警执行器拥有一个报警频率区间,通过报警频率映射到报警执行器的区间来选择对应的AlarmExecutor,这就是系统定义的报警规则

II. 报警规则解析

通过前面的报警规则的简单说明,基本上也可以捞出报警规则的解析原则了

  • 每种报警类型,对应一个报警规则
  • 每个报警规则中,可以有多个报警执行器
  • 每个报警执行器都有一个对应的报警频率的阀值
  • 根据阀值对所有的报警执行器排序
  • 计算报警频率,映射到哪个区间,则选择哪个报警执行器

上面是一个简单的解析规则,当然实际上和这个差不多,但有一些问题需要额外注意

  1. 只想选择一种报警方式,是否可以支持?
  2. 多重报警方式同时调用怎么处理?(如我希望用短信提示说有问题,同时用邮件包含详细的异常堆栈)
  3. 频率限制
  4. 报警类型没有设置报警规则如何处理?
  5. 报警规则中使用了一个未注册的报警执行器会怎样?

1. 实现方案说明

再次将报警规则类拿出来看一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 报警用户
*/
private List<String> users;


/**
* 报警的阀值
*/
private List<AlarmThreshold> alarmThreshold;


/**
* 最小的报警数
*/
private int minLimit;


/**
* 最大的报警数
*/
private int maxLimit;


/**
* 报警类型 {@link IExecute#getName()}
*/
private String alarmLevel;


/**
* true 表示当报警超过当前的阀值之后, 将提升报警的程度
*/
private boolean autoIncEmergency;

针对上面的问题,逐一说明

  • 首先是 autoIncEmergency 这个参数,如果为true,则表示可以走上面的哪个区间映射的规则;否则就全部走AlarmConfig中默认的报警类型了
  • minLimit : 表示发生报警的频率下限值,小于这个值就不会执行具体的报警逻辑
  • maxLimit : 最大的报警频率,超过了也不报警(简单的频率控制)
  • alarmLevel: 对应的就是具体的报警类型
  • alarmThreshold: 这个只有在autoIncEmergency=true时,才有小,也就是我们前面说的不同的报警执行器,根据阀值区间进行排序,开启之后,遍历,判断频率是否在这个区间内,若在,则表示可以选择它了
  • 如果不存在报警规则,则采用默认的兜底规则
  • 若报警执行器也不存在,就直接采用系统定义的日志报警执行器

2. 实现

基本上前面已经将整个逻辑都说了,所以实际的编码反而比较清晰了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
* 获取具体的报警执行器
* <p>
* 1. 未开启严重等级上升时, 直接返回
* 2. 开启之后, 判断当前的计数 范围
*
* @param alarmConfig 报警配置项, 内部所有的参数都不可能为null
*/
public static ExecuteHelper getExecute(final AlarmConfig alarmConfig,
int count) {
// 未达到报警的下限 or 超过报警的上限时
if (count < alarmConfig.getMinLimit() || count > alarmConfig.getMaxLimit()) {
return new ExecuteHelper(SimpleExecuteFactory.getExecute(NoneExecute.NAME),
alarmConfig.getUsers());
}

// 未开启报警升级, 直接返回
if (!alarmConfig.isAutoIncEmergency()) {
return new ExecuteHelper(SimpleExecuteFactory.
getExecute(alarmConfig.getAlarmLevel()),
alarmConfig.getUsers());
}


// 报警等级开启上升之趋势
// 1. 获取设置的默认等级
// 2. 判断当前的报警次数, 选择对应的报警类型
// 3. 选择具体的报警类型
String defaultLevel = alarmConfig.getAlarmLevel();
String selectLevel = null;
List<String> selectUser = alarmConfig.getUsers();

List<AlarmThreshold> list = alarmConfig.getAlarmThreshold();
boolean useNew = false;
boolean containDefaultLevel = false;
for (AlarmThreshold alarmThreshold : list) {
if (Objects.equals(alarmThreshold.getAlarmLevel(), defaultLevel)) {
containDefaultLevel = true;
}
}


for (AlarmThreshold alarmThreshold : list) {
// 表示当前的报警等级已经赶上默认的报警等级了, 所以要选择新的报警类型
if (Objects.equals(alarmThreshold.getAlarmLevel(), defaultLevel)) {
useNew = true;
}

if (count < alarmThreshold.getThreshold()) {
break;
}

selectLevel = alarmThreshold.getAlarmLevel();
// 选择新的报警类型时, 需要更新报警用户
selectUser = alarmThreshold.getUsers();
}


// 阀值列表中不包含默认报警类型,则根据新的来
if (!containDefaultLevel && selectLevel != null) {
return new ExecuteHelper(SimpleExecuteFactory.getExecute(selectLevel), selectUser);
}


// 如果阀值列表中包含了默认报警类型, 且已经超过默认阀值
if (useNew && selectLevel != null) {
return new ExecuteHelper(SimpleExecuteFactory.getExecute(selectLevel), selectUser);
} else {
return new ExecuteHelper(SimpleExecuteFactory.getExecute(defaultLevel), alarmConfig.getUsers());
}
}

具体的实现基本和我们前面分析的一样,但有一个地方需要额外注意

  • 默认报警阀值,可以直接决定是否需要报警
  • 因此定义的其他报警方式的阀值,应该在默认的阀值区间内
  • 当然AlarmThreshold中不包含默认报警方式时,优先选择阀值区间的报警方式
  • 当然AlarmThreshold中包含默认报警方式时,根据新的规则做处理

(吐槽:上面这个实现有点绕,后面想办法规避下,搞得不太好理解了)

另外一个问题就是,上面的实现没有支持可以同时选择多个报警执行器的情况

因为考虑到后面肯定会对报警规则的定义和解析放开,所以先实现了一个简单的场景,具体的放在后面处理

III. 小结

到这里报警规则和报警执行器之间的解析关系已确定,剩下的东西就简单了,一个维持报警频率计数,一个报警线程池,再加上一个对外接口的封装而言

基本上,到这里主要的核心逻辑已经完成,小结一下本系统中的核心设计理念 – 一切可自定义(当然目前差得有点远)

1. 报警执行器

  • 通过SPI机制支持用户自定义扩展
  • 要求 Executor 拥有唯一标识
  • 因为报警执行器支持扩展,所以Executor的内部实现,完全可以由用户决定

2. 报警规则

  • 目前报警规则只提供默认的基于频率区间的选择方案
  • 报警规则通过报警执行器的name与之唯一对应,若对应不上,则选择默认执行器
  • 报警规则的加载同样基于SPI,支持自定义,因此报警规则可以存在任何地方
  • 报警规则加载器,提供一个报警规则变动的钩子(load()),若采用自定义的加载类,则确保规则变动时,主动回调这个方法
  • 默认的报警规则加载类,是基于系统的配置文件实现,内部托管了文件的变动更新事件(使用commons-io实现)

IV. 其他

相关博文

  1. 报警系统QuickAlarm总纲
  2. 报警系统QuickAlarm之报警执行器的设计与实现
  3. 报警系统QuickAlarm之报警规则的设定与加载
  4. 报警系统QuickAlarm之报警规则解析
  5. 报警系统QuickAlarm之频率统计及接口封装
  6. 报警系统QuickAlarm使用手册
  7. 报警系统QuickAlarm之默认报警规则扩展

项目: QuickAlarm

个人博客: Z+|blog

基于hexo + github pages搭建的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见识有限,如发现bug或者有更好的建议,随时欢迎批评指正,我的微博地址: 小灰灰Blog

扫描关注

QrCode

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×