監控指標採集(WarningLog) -> 規則驗證 (WarningRuleRecord)-> 通知記錄(WarningEvent)
報警規則
報警的觸發條件和通知方式。
報警事件 WarningRuleRecord
系統每隔1分鐘,就會根據報警規則中設置的報警觸發條件,判斷指標是否觸發報警。如果觸發,則會生成一個報警事件記錄。
通知記錄 WarningEvent
報警事件生成之後,系統會根據報警規則中設置的報警生效時段和報警間隔,判斷是否發送報警通知(電話、短信、釘釘羣機器人)給您。如果發送,則會生成一個通知記錄。
閾值報警
當前指標的值和閾值實時比較,如果符合設定的閾值條件,則觸發報警。
1.0 監控平臺之前出過的問題
雖然 之前讓公元優化定時輪詢的執行線程池,理論上所有定時任務都會在同一個線程池的不同線程上-一定程度上解決了之前的告警事件延時問題。默認是所有定時輪詢都是一個線程執行。 導致告警事件延時。出現大量延時重複數據。
1.1 監控指標採集
WarningLog metric 收集表
type =1 業務上隊列積壓監控 (輪詢調用隊列服務,拉取)
type =2 來自隊列監控 ,(推送,這個目前只有一個指標,沒有插件開發收集指標問題。java這邊會開發收集tps ,rt 等指標插件,詳情請看https://www.jianshu.com/p/28d25a6637ff)
type =3 來自採集器(拉取)
監控設計上拉取就有問題 ,70個線上指標就出現了線上指標延遲問題。如果有上萬個線上指標呢。
通過分佈式lock ,定時輪詢監控指標通過3種不同方式,進行收集批量寫入WarningLog 表 。
性能上 ,不管有多少pod , 同時執行的只有一個pod的一個線程池 。性能上,無擴展的能力 。
而且目前收集都採用的拉的方式,一旦某個服務不可用,會阻塞當前執行線程。收集的數據量很大的情況 ,必然會導致數據收集端的延時 。所以線上對接的業務指標還不是很多,大概60,70個。沒有服務metric 指標等,和以前公司幾十萬個服務監控指標差距還是很大的,目前作用其實也不大。
1.2 監控規則
監控規則採用 自定義 @Async 方式,執行規則將符合規則的告警數據插入告警規則表WarningRuleRecord中, @Async大量存在與j37代碼中。所有異步接口代碼都公用一個線程池 ,隨着未來規則越來越多,必然會存在着上一個監控規則輪詢沒有執行完 ,下個規則輪詢就開始的問題.隨着累積的加劇 ,後期必然會影響其他的異步方法調用。
目前線上已經出現了積壓情況。值得警惕
j37-51-6988d5667c-dtc22 | online | 10.66.1.207 | cn-zhangjiakou.10.63.97.203 | 線程池 async-service- 中隊列積壓任務數量爲 71
1.3 告警
通過告警規則找到對應告警規則記錄WarningRuleRecord,通過告警通知合併策略以及通知策略。下推通知WarningEvent給用戶
代碼規範真的蠻弱的 , 0,6 表示類別,狀態, 還是蠻難看懂的
query.setIsDelete(0);
query.setStatus(0);
query.setCreateDate(beforeTime);
query.setRuleId(warningRule.getId());
query.setWarningServiceId(warningRule.getWarningServiceId());
List<WarningEvent> eventList = eventMapper.queryByWarningServiceId(query);
if (CollectionUtils.isNotEmpty(eventList)) {
for (WarningEvent item : eventList) {
WarningEventOperation eventOperation = new WarningEventOperation();
eventOperation.setIsDelete(0);
eventOperation.setEventId(item.getId());
eventOperation.setType(6);
1.4 過期數據清除
雖然通過分佈式lock ,但依然會有多個pod 同時執行dataclean任務,這裏讓我不得不懷疑分佈式lock 有沒有作用。
從清除數據上可知 ,每天凌晨2點線上清除的數據在 16w 級別 ,數據量不大。
1.5 通知流程
下面是實現代碼
/**
* 每天凌晨2:00清理數據
*/
@Scheduled(cron = "0 0 2 * * ?")
private void dataClean() {
redisLockUtil.executeWithLock("data:clean:cron:Lock", () -> {
// 定時清理主無效的(告警規則運行記錄)
warningCronService.dataClean();
});
}
/**
* 整分鐘00秒運行
*/
@Scheduled(cron = "0 */1 * * * ?")
private void executeLogCollectCron() {
redisLockUtil.executeWithLock("warning:Collect:Execute:Lock111", () -> {
// 告警規則運行週期(整分鐘00秒運行)
warningCronService.excute();
//告警日誌數據採集器(週期性採集數據)
warningCronService.executeLogCollect();
//防止異步方法過快釋放分佈式鎖 (任務運行多次)
waitReleaseLock(1);
});
}
/**
* 整分鐘00秒運行
*/
@Scheduled(cron = "0 */1 * * * ?")
private void executeLogCollectCron() {
redisLockUtil.executeWithLock("warning:Collect:Execute:Lock111", () -> {
// 告警規則運行週期(整分鐘00秒運行)
warningCronService.excute();
//告警日誌數據採集器(週期性採集數據)
warningCronService.executeLogCollect();
//防止異步方法過快釋放分佈式鎖 (任務運行多次)
waitReleaseLock(1);
});
}
/**
* 整分鐘30秒運行
*/
@Scheduled(cron = "30 */1 * * * ?")
private void executeExcuteCron() {
redisLockUtil.executeWithLock("warning:Excute:Execute:Lock", () -> {
//每1分鐘通過接口調用,獲取隊列積壓數據&(記錄最大歷史積壓值)
warningCronService.executeQueueWarning();
});
}
/**
* 每三十秒檢測是否有待接收事件需要發送消息(包括輪詢消息和合並後消息)
*/
@Scheduled(cron = "0/30 * * * * ?")
private void sendQueueMessage() {
redisLockUtil.executeWithLock("send:Queue:Message", () -> {
// 三十秒一次檢測隊列事件是否超過合併週期(超過週期就發送預警消息)
warningCronService.sendQueueMessage();
// 三十秒一次檢測普通事件是否超過合併週期(超過週期就發送預警消息)
warningCronService.sendMessage();
// 輪詢發送消息
warningCronService.sendMoreMessage();
});
}
/**
* 每天凌晨2:00清理數據
*/
@Scheduled(cron = "0 0 2 * * ?")
private void dataClean() {
redisLockUtil.executeWithLock("data:clean:cron:Lock", () -> {
// 定時清理主無效的(告警規則運行記錄)
warningCronService.dataClean();
});
}
private void waitReleaseLock(int i) {
try {
Thread.sleep(i * 1000);
} catch (Exception e) {
}
}
/**
* 從隊列裏面獲取
*/
@Scheduled(cron = "0/30 * * * * ?")
public void queueCollect() {
if (!queueCollectFlag) {
return;
}
redisLockUtil.executeWithLock(QUEUE_COLLECT_REDIS_KEY, () -> {
warningCronService.exportLogFromQueue();
});
}