openfalcon源码分析之Judge
本节内容
- Judge功能
- 源码分析
- 设计优缺点
1. Judge功能
在open-falcon中,Judge模块的功能是经过从HBS上同步告警的strategys(告警策略),及Expression,用来在接收transfer上报过来的数据时,对数据进行检测,每收到一条上报的数据,就去匹配对应的strategy和Expression是否知足条件,判断是否须要向redis发送告警。golang
告警策略
open-falcon中的策略有两类,strategys和Expression。strategys与主机进行管线,能够经过一个主机找到其对应的全部strategys。而Expression则不须要有任何关联,是对全局范围内进行的一个过滤和判断。redis
stratrgys查找策略
从transfer上接收过来的数据带有endpoint,经过主机能够找到其对应的主机组(主机和主机组是多对多的关系),经过主机组能够找到对应的模板组(模板和主机组是多对多的关系),模板之间又可能有继承关系,经过模板组能够找到对应的strategy(模板和strategy是一对多的关系)一个模板就是一组strategy的集合。这样就能够经过上报过来的数据的endpoint找到其对应的全部的strategys了。express
expression查找策略
expression不和主机关联,其针对的是全局的配置,主要对应一些须要特殊定制的告警检查。数据结构
2. 源码分析
处理流程分析
接收数据方
- 经过RPC注册两个方法,一个是ping,返回一个nil的返回值,另外一个是send,处理transfer发送过来的数据。
- 对于transfer发送过来的每一个监控数据,都会生成一个pk(对数据的endpoint,metric,tags进行字符串拼接,以后再用md5进行hash,生成一个hash值)。经过pk的前两位hash值,去HistoryBigMap中找到对应的值,调用PushFrontAndMaintain。(HistoryBigMap的结构会在后面进行讲解)
- 调用PushFrontAndMaintain会去JudgeItemMap中找到pk对应的值(链表)
- Judge方法会调用CheckStrategy方法以及CheckExpression方法对新监控数据和历史数据进行处理。
- CheckStrategy方法先将监控数据中的endpoint和metric进行字符串拼接成key,经过key去strategys中寻找对应的strategys列表。对于一个监控数据若是找到了多个strategy,则会对每一个strategy的tags进行匹配,若和监控数据不符合则被过滤掉。剩下的每一个strategys将会调用judgeItemWithStrategy方法,该方法先将strategy的表达式进行拼接找到对应的函数,而后将历史数据传递到Compute中进行逻辑判断,若是判断匹配则返回的值isTriggered为true,不然返回false.在judgeItemWithStrategy中会建立event事件,调用sendEventIfNeed方法判断该event是否应该发送出去。
- sendEventIfNeed方法会去检查LastEvents中该event是否已经存在,并按照必定规则判断是否须要调用sendEvent将event发送到redis中。源码以下.
- CheckExpression方法先将监控数据中的endpoint、metric、tags进行字符串拼接成key,再用该key在ExpressionMap字典中检查是否有对应的expressions,若是找到则调用filterRelatedExpressions进行处理
- filterRelatedExpressions检查全部对应的expressions,筛选掉tags不匹配的expressions,剩下的就是全部符合条件的expressions,针对全部符合条件的expressions,调用judgeItemWithExpression构建告警event,后面的处理步骤和strategys一致。
// sendEventIfNeed方法中判断event是否该发告警的逻辑
if isTriggered {
event.Status = "PROBLEM"
if !exists || lastEvent.Status[0] == 'O' {
// 本次触发了阈值,以前又没报过警,得产生一个报警Event
event.CurrentStep = 1
// 可是有些用户把最大报警次数配置成了0,至关于屏蔽了,要检查一下
if maxStep == 0 {
return
}
sendEvent(event)
return
}
// 逻辑走到这里,说明以前Event是PROBLEM状态
if lastEvent.CurrentStep >= maxStep {
// 报警次数已经足够多,到达了最多报警次数了,再也不报警
return
}
if historyData[len(historyData)-1].Timestamp <= lastEvent.EventTime {
// 产生过报警的点,就不能再使用来判断了,不然容易出现一分钟报一次的状况
// 只须要拿最后一个historyData来作判断便可,由于它的时间最老
return
}
if now-lastEvent.EventTime < g.Config().Alarm.MinInterval {
// 报警不能太频繁,两次报警之间至少要间隔MinInterval秒,不然就不能报警
return
}
event.CurrentStep = lastEvent.CurrentStep + 1
sendEvent(event)
} else {
// 若是LastEvent是Problem,报OK,不然啥都不作
if exists && lastEvent.Status[0] == 'P' {
event.Status = "OK"
event.CurrentStep = 1
sendEvent(event)
}
}
同步strategy和expressions方
main函数中起了go-routine 专门用来从hbs同步strategy和expressions,同步完以后从新组装成StrategyMap和ExpressionMap两个字典。源码分析
几个数据结构分析
- HistoryBigMap
- HistoryBigMap是个大字典,key是pk的hash值的前两位,一共有16*16=256个键,value是NewJudgeItemMap,也是一个字典,该字典的key是pk,value对应历史数据组成的链表,链表最大长度可在配置文件中的remain配置,默认是11,也就是默认对每一个数据,保留11个历史数据节点。
- StrategyMap
- StrategyMap是个字典,key是hostname和Metric拼接的字符串,value是一个array,由于同一个hostname和Metric可能对应多个Strategy(可能tags不一样,固然可能没有tags时,父子模板中都有该Strategy)
- ExpressionMap
- ExpressionMap是个字典,key是metric和tags拼接的字符串,value是一个array,metric和tags没法惟一肯定expression。
- LastEvents
- LastEvents是个字典,key是event.Id,该id是由strategy.id和pk的hash值拼接产生的,能够惟一肯定一个数据,因此value就是event事件自己。
3. 设计优缺点
优势:设计
- 支持告警间隔以及最大告警次数,太多的告警并无什么帮助,何况会将真正有用的告警淹没在无心义的告警中。
- 支持Expression,zabbix是将告警和主机绑定起来,某些并不和某个主机关联的告警没法作,有了Expression以后就能够关联那一类告警了。
缺点:code
- 只支持按照次数告警,若是支持按照时间告警就行了,好比某台机器连续五分钟cpu负载太高告警。