openfalcon源碼分析之Judge
本節內容
- Judge功能
- 源碼分析
- 設計優缺點
1. Judge功能
在open-falcon中,Judge模塊的功能是通過從HBS上同步告警的strategys(告警策略),及Expression,用來在接收transfer上報過來的數據時,對數據進行檢測,每收到一條上報的數據,就去匹配對應的strategy和Expression是否滿足條件,判斷是否需要向redis發送告警。
告警策略
open-falcon中的策略有兩類,strategys和Expression。strategys與主機進行管線,可以通過一個主機找到其對應的所有strategys。而Expression則不需要有任何關聯,是對全局范圍內進行的一個過濾和判斷。
stratrgys查找策略
從transfer上接收過來的數據帶有endpoint,通過主機可以找到其對應的主機組(主機和主機組是多對多的關系),通過主機組可以找到對應的模板組(模板和主機組是多對多的關系),模板之間又可能有繼承關系,通過模板組可以找到對應的strategy(模板和strategy是一對多的關系)一個模板就是一組strategy的集合。這樣就可以通過上報過來的數據的endpoint找到其對應的所有的strategys了。
expression查找策略
expression不和主機關聯,其針對的是全局的配置,主要對應一些需要特殊定制的告警檢查。
2. 源碼分析
處理流程分析
接收數據方
- 通過RPC注冊兩個方法,一個是ping,返回一個nil的返回值,另一個是send,處理transfer發送過來的數據。
- 對於transfer發送過來的每個監控數據,都會生成一個pk(對數據的endpoint,metric,tags進行字符串拼接,之后再用md5進行hash,生成一個hash值)。通過pk的前兩位hash值,去HistoryBigMap中找到對應的值,調用PushFrontAndMaintain。(HistoryBigMap的結構會在后面進行講解)
- 調用PushFrontAndMaintain會去JudgeItemMap中找到pk對應的值(鏈表)
-
若該pk能找到值,則調用該鏈表的PushFrontAndMaintain方法,將新到來的監控數據和鏈表最大長度傳遞進去。鏈表的PushFrontAndMaintain方法將新監控數據插入到鏈表的頭部,如果超過了最大長度,則把最老的數據刪除。再調用Judge方法對新監控數據和歷史數據進行判斷。
-
若沒找到值,則會新創建一個鏈表,把新監控數據放入鏈表中。再調用Judge方法對新監控數據和歷史數據進行判斷。
-
- 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一致。
- CheckStrategy方法先將監控數據中的endpoint和metric進行字符串拼接成key,通過key去strategys中尋找對應的strategys列表。對於一個監控數據如果找到了多個strategy,則會對每個strategy的tags進行匹配,若和監控數據不符合則被過濾掉。剩下的每個strategys將會調用judgeItemWithStrategy方法,該方法先將strategy的表達式進行拼接找到對應的函數,然后將歷史數據傳遞到Compute中進行邏輯判斷,如果判斷匹配則返回的值isTriggered為true,否則返回false.在judgeItemWithStrategy中會創建event事件,調用sendEventIfNeed方法判斷該event是否應該發送出去。
// 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之后就可以關聯那一類告警了。
缺點:
- 只支持按照次數告警,如果支持按照時間告警就好了,比如某台機器連續五分鍾cpu負載過高告警。