報警系統QuickAlarm之頻率統計及介面封裝

一灰灰發表於2018-03-04

前面將報警規則的制定載入解析,以及報警執行器的定義載入和擴充套件進行了講解,基本上核心的內容已經完結,接下來剩下內容就比較簡單了

  • 報警頻率的統計
  • 報警執行緒池
  • 對外封裝統一可用的解耦

I. 報警頻率統計

1. 設計

前面在解析報警規則時,就有一個count引數,用來確定具體選擇什麼報警執行器的核心引數,我們維護的方法也比較簡單:

  • 針對報警型別,進行計數統計,沒呼叫一次,則計數+1
  • 每分鐘清零一次

2. 實現

因為每種報警型別,都維護一個獨立的計數器

定義一個map來儲存對應關係

private ConcurrentHashMap<String, AtomicInteger> alarmCountMap;
複製程式碼

每分鐘執行一次清零

// 每分鐘清零一把報警計數
ScheduledExecutorService scheduleExecutorService = Executors.newScheduledThreadPool(1);
scheduleExecutorService.scheduleAtFixedRate(() -> {
    for (Map.Entry<String, AtomicInteger> entry : alarmCountMap.entrySet()) {
        entry.getValue().set(0);
    }
}, 0, 1, TimeUnit.MINUTES);
複製程式碼

注意上面的實現,就有什麼問題?

有沒有可能因為map中的資料過大(或者gc什麼原因),導致每次清零花不少的時間,而導致計數不準呢? (先不給出回答)

計數加1操作

/**
 * 執行緒安全的獲取報警總數 並自動加1
 *
 * @param key
 * @return
 */
private int getAlarmCount(String key) {
    if (!alarmCountMap.containsKey(key)) {
        synchronized (this) {
            if (!alarmCountMap.containsKey(key)) {
                alarmCountMap.put(key, new AtomicInteger(0));
            }
        }
    }

    return alarmCountMap.get(key).addAndGet(1);
}
複製程式碼

II. 報警執行緒池

目前也只是提供了一個非常簡單的執行緒池實現,後面的考慮是抽象一個基於forkjoin的併發框架來處理(主要是最近接觸到一個大神基於forkjoin寫的併發器元件挺厲害的,所以等我研究透了,山寨一個)

// 報警執行緒池
private ExecutorService alarmExecutorService = new ThreadPoolExecutor(3, 5, 60,
        TimeUnit.SECONDS,
        new LinkedBlockingDeque<>(10), 
        new DefaultThreadFactory("sms-sender"),
        new ThreadPoolExecutor.CallerRunsPolicy());
複製程式碼

任務提交執行

private void doSend(final ExecuteHelper executeHelper, 
  final AlarmContent alarmContent) {
    alarmExecutorService.execute(() ->
      executeHelper.getIExecute().sendMsg(
        executeHelper.getUsers(), 
        alarmContent.getTitle(), 
        alarmContent.getContent()));
}
複製程式碼

III. 介面封裝

這個就沒什麼好說的了

public void sendMsg(String key, String content) {
    sendMsg(new AlarmContent(key, null, content));
}


public void sendMsg(String key, String title, String content) {
    sendMsg(new AlarmContent(key, title, content));
}

/**
 * 1. 獲取報警的配置項
 * 2. 獲取當前報警的次數
 * 3. 選擇適當的報警型別
 * 4. 執行報警
 * 5. 報警次數+1
 *
 * @param alarmContent
 */
private void sendMsg(AlarmContent alarmContent) {
    try {
        // get alarm config
        AlarmConfig alarmConfig = confLoader.getAlarmConfig(alarmContent.key);

        // get alarm count
        int count = getAlarmCount(alarmContent.key);
        alarmContent.setCount(count);


        ExecuteHelper executeHelper;
        if (confLoader.alarmEnable()) { // get alarm execute
            executeHelper = AlarmExecuteSelector.getExecute(alarmConfig, count);
        } else {  // 報警關閉, 則走空報警流程, 將報警資訊寫入日誌檔案
            executeHelper = AlarmExecuteSelector.getDefaultExecute();
        }


        // do send msg
        doSend(executeHelper, alarmContent);
    } catch (Exception e) {
        logger.error("AlarmWrapper.sendMsg error! content:{}, e:{}", alarmContent, e);
    }
}
複製程式碼

介面封裝完畢之後如何使用呢?

我們使用單例模式封裝了唯一對外使用的類AlarmWrapper,使用起來也比較簡單,下面就是一個測試case

@Test
public void sendMsg() throws InterruptedException {
    String key = "NPE";
    String title = "NPE異常";
    String msg = "出現NPE異常了!!!";

    AlarmWrapper.getInstance().sendMsg(key, title, msg);  // 微信報警

    // 不存在異常配置型別, 採用預設報警, 次數較小, 則直接部署出
    AlarmWrapper.getInstance().sendMsg("zzz", "不存在xxx異常配置", "報警嗒嗒嗒嗒");
    
    Thread.sleep(1000);
}
複製程式碼

使用起來比較簡單,就那麼一行即可,從這個使用也可以知道,整個初始化,就是在這個物件首次被訪問時進行

建構函式內容如下:

private AlarmWrapper() {
  // 記錄每種異常的報警數
  alarmCountMap = new ConcurrentHashMap<>();

  // 載入報警配置資訊
  confLoader = ConfLoaderFactory.loader();

  // 初始化執行緒池
  initExecutorService();
}
複製程式碼

所有如果你希望在自己的應用使用之前就載入好所有的配置,不妨提前執行一下 AlarmWrapper.getInstance()

IV. 小結

基於此,整個系統設計基本上完成,當然程式碼層面也ok了,剩下的就是使用手冊了

再看一下我們的整個邏輯,基本上就是下面這個流程了

IMAGE

  1. 提交報警
  • 封裝報警內容(報警型別,報警主題,報警內容)
  • 維護報警計數(每分鐘計數清零,每個報警型別對應一個報警計數)
  1. 選擇報警
  • 根據報警型別選擇報警規則
  • 根據報警規則,和當前報警頻率選擇報警執行器
    • 若不開啟區間對映,則返回預設執行器
    • 否則遍歷所有執行器的報警頻率區間,選擇匹配的報警規則
  1. 執行報警
  • 封裝報警任務,提交執行緒池
  • 報警執行器內部實現具體報警邏輯

V. 其他

相關博文

  1. 報警系統QuickAlarm總綱
  2. 報警系統QuickAlarm之報警執行器的設計與實現
  3. 報警系統QuickAlarm之報警規則的設定與載入
  4. 報警系統QuickAlarm之報警規則解析
  5. 報警系統QuickAlarm之頻率統計及介面封裝
  6. 報警系統QuickAlarm使用手冊

專案: QuickAlarm

個人部落格: Z+|blog

基於hexo + github pages搭建的個人部落格,記錄所有學習和工作中的博文,歡迎大家前去逛逛

宣告

盡信書則不如,已上內容,純屬一家之言,因本人能力一般,見識有限,如發現bug或者有更好的建議,隨時歡迎批評指正,我的微博地址: 小灰灰Blog

掃描關注

QrCode

相關文章