報警系統(tǒng)QuickAlarm之頻率統(tǒng)計及接口封裝
前面將報警規(guī)則的制定加載解析牵署,以及報警執(zhí)行器的定義加載和擴(kuò)展進(jìn)行了講解,基本上核心的內(nèi)容已經(jīng)完結(jié),接下來剩下內(nèi)容就比較簡單了
- 報警頻率的統(tǒng)計
- 報警線程池
- 對外封裝統(tǒng)一可用的解耦
I. 報警頻率統(tǒng)計
1. 設(shè)計
前面在解析報警規(guī)則時,就有一個count參數(shù)悬而,用來確定具體選擇什么報警執(zhí)行器的核心參數(shù),我們維護(hù)的方法也比較簡單:
- 針對報警類型锭汛,進(jìn)行計數(shù)統(tǒng)計笨奠,沒調(diào)用一次,則計數(shù)+1
- 每分鐘清零一次
2. 實(shí)現(xiàn)
因?yàn)槊糠N報警類型唤殴,都維護(hù)一個獨(dú)立的計數(shù)器
定義一個map來存儲對應(yīng)關(guān)系
private ConcurrentHashMap<String, AtomicInteger> alarmCountMap;
每分鐘執(zhí)行一次清零
// 每分鐘清零一把報警計數(shù)
ScheduledExecutorService scheduleExecutorService = Executors.newScheduledThreadPool(1);
scheduleExecutorService.scheduleAtFixedRate(() -> {
for (Map.Entry<String, AtomicInteger> entry : alarmCountMap.entrySet()) {
entry.getValue().set(0);
}
}, 0, 1, TimeUnit.MINUTES);
注意上面的實(shí)現(xiàn)般婆,就有什么問題?
有沒有可能因?yàn)閙ap中的數(shù)據(jù)過大(或者gc什么原因)朵逝,導(dǎo)致每次清零花不少的時間蔚袍,而導(dǎo)致計數(shù)不準(zhǔn)呢? (先不給出回答)
計數(shù)加1操作
/**
* 線程安全的獲取報警總數(shù) 并自動加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. 報警線程池
目前也只是提供了一個非常簡單的線程池實(shí)現(xiàn)配名,后面的考慮是抽象一個基于forkjoin的并發(fā)框架來處理(主要是最近接觸到一個大神基于forkjoin寫的并發(fā)器組件挺厲害的啤咽,所以等我研究透了,山寨一個)
// 報警線程池
private ExecutorService alarmExecutorService = new ThreadPoolExecutor(3, 5, 60,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(10),
new DefaultThreadFactory("sms-sender"),
new ThreadPoolExecutor.CallerRunsPolicy());
任務(wù)提交執(zhí)行
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. 獲取報警的配置項(xiàng)
* 2. 獲取當(dāng)前報警的次數(shù)
* 3. 選擇適當(dāng)?shù)膱缶愋? * 4. 執(zhí)行報警
* 5. 報警次數(shù)+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 { // 報警關(guān)閉, 則走空報警流程, 將報警信息寫入日志文件
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 = "出現(xiàn)NPE異常了!!!";
AlarmWrapper.getInstance().sendMsg(key, title, msg); // 微信報警
// 不存在異常配置類型, 采用默認(rèn)報警, 次數(shù)較小, 則直接部署出
AlarmWrapper.getInstance().sendMsg("zzz", "不存在xxx異常配置", "報警嗒嗒嗒嗒");
Thread.sleep(1000);
}
使用起來比較簡單芋膘,就那么一行即可鳞青,從這個使用也可以知道霸饲,整個初始化,就是在這個對象首次被訪問時進(jìn)行
構(gòu)造函數(shù)內(nèi)容如下:
private AlarmWrapper() {
// 記錄每種異常的報警數(shù)
alarmCountMap = new ConcurrentHashMap<>();
// 加載報警配置信息
confLoader = ConfLoaderFactory.loader();
// 初始化線程池
initExecutorService();
}
所有如果你希望在自己的應(yīng)用使用之前就加載好所有的配置臂拓,不妨提前執(zhí)行一下 AlarmWrapper.getInstance()
IV. 小結(jié)
基于此厚脉,整個系統(tǒng)設(shè)計基本上完成,當(dāng)然代碼層面也ok了埃儿,剩下的就是使用手冊了
再看一下我們的整個邏輯器仗,基本上就是下面這個流程了
- 提交報警
- 封裝報警內(nèi)容(報警類型融涣,報警主題童番,報警內(nèi)容)
- 維護(hù)報警計數(shù)(每分鐘計數(shù)清零,每個報警類型對應(yīng)一個報警計數(shù))
- 選擇報警
- 根據(jù)報警類型選擇報警規(guī)則
- 根據(jù)報警規(guī)則威鹿,和當(dāng)前報警頻率選擇報警執(zhí)行器
- 若不開啟區(qū)間映射剃斧,則返回默認(rèn)執(zhí)行器
- 否則遍歷所有執(zhí)行器的報警頻率區(qū)間,選擇匹配的報警規(guī)則
- 執(zhí)行報警
- 封裝報警任務(wù)忽你,提交線程池
- 報警執(zhí)行器內(nèi)部實(shí)現(xiàn)具體報警邏輯
V. 其他
相關(guān)博文
- 報警系統(tǒng)QuickAlarm總綱
- 報警系統(tǒng)QuickAlarm之報警執(zhí)行器的設(shè)計與實(shí)現(xiàn)
- 報警系統(tǒng)QuickAlarm之報警規(guī)則的設(shè)定與加載
- 報警系統(tǒng)QuickAlarm之報警規(guī)則解析
- 報警系統(tǒng)QuickAlarm之頻率統(tǒng)計及接口封裝
- 報警系統(tǒng)QuickAlarm使用手冊
項(xiàng)目: QuickAlarm
- 項(xiàng)目地址: Quick-Alarm
- 博客地址: 小灰灰Blog
個人博客: Z+|blog
基于hexo + github pages搭建的個人博客幼东,記錄所有學(xué)習(xí)和工作中的博文,歡迎大家前去逛逛
聲明
盡信書則不如科雳,已上內(nèi)容根蟹,純屬一家之言,因本人能力一般糟秘,見識有限简逮,如發(fā)現(xiàn)bug或者有更好的建議,隨時歡迎批評指正尿赚,我的微博地址: 小灰灰Blog