博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
报警系统QuickAlarm之频率统计及接口封装
阅读量:6835 次
发布时间:2019-06-26

本文共 3912 字,大约阅读时间需要 13 分钟。

前面将报警规则的制定加载解析,以及报警执行器的定义加载和扩展进行了讲解,基本上核心的内容已经完结,接下来剩下内容就比较简单了

  • 报警频率的统计
  • 报警线程池
  • 对外封装统一可用的解耦

I. 报警频率统计

1. 设计

前面在解析报警规则时,就有一个count参数,用来确定具体选择什么报警执行器的核心参数,我们维护的方法也比较简单:

  • 针对报警类型,进行计数统计,没调用一次,则计数+1
  • 每分钟清零一次

2. 实现

因为每种报警类型,都维护一个独立的计数器

定义一个map来存储对应关系

private ConcurrentHashMap
alarmCountMap;复制代码

每分钟执行一次清零

// 每分钟清零一把报警计数ScheduledExecutorService scheduleExecutorService = Executors.newScheduledThreadPool(1);scheduleExecutorService.scheduleAtFixedRate(() -> {    for (Map.Entry
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

@Testpublic 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了,剩下的就是使用手册了

再看一下我们的整个逻辑,基本上就是下面这个流程了

  1. 提交报警
  • 封装报警内容(报警类型,报警主题,报警内容)
  • 维护报警计数(每分钟计数清零,每个报警类型对应一个报警计数)
  1. 选择报警
  • 根据报警类型选择报警规则
  • 根据报警规则,和当前报警频率选择报警执行器
    • 若不开启区间映射,则返回默认执行器
    • 否则遍历所有执行器的报警频率区间,选择匹配的报警规则
  1. 执行报警
  • 封装报警任务,提交线程池
  • 报警执行器内部实现具体报警逻辑

V. 其他

相关博文

项目: QuickAlarm

  • 项目地址:
  • 博客地址:

个人博客:

基于hexo + github pages搭建的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

声明

尽信书则不如,已上内容,纯属一家之言,因本人能力一般,见识有限,如发现bug或者有更好的建议,随时欢迎批评指正,我的微博地址:

扫描关注

转载地址:http://rrqkl.baihongyu.com/

你可能感兴趣的文章
Python技巧之函数拆包裹
查看>>
创建用户
查看>>
在spring web中启动mqtt
查看>>
QingStor 对象存储服务正式商用
查看>>
查看 SELinux状态及关闭SELinux
查看>>
(转) Twisted :第十八部分 Deferreds 全貌
查看>>
Oracle 基础之数据库管理
查看>>
Web应用中的缓存一致性问题
查看>>
通过Android重审GET和POST请求
查看>>
马王堆汉墓帛书‧老子甲本——道经
查看>>
ruby中DBI连接MySQL数据库步骤详解
查看>>
mongodb 的PHP 扩展
查看>>
bp神经网络
查看>>
彻底理解cookie,session,localStorage(附代码)
查看>>
你还记得当初为什么进入IT行业吗?
查看>>
[翻译]MongoDb 架构(MongoDb Architecture)
查看>>
oracle统计数据库所有表的数据记录数SQL
查看>>
Kafka 安装配置及快速入门
查看>>
随机森林案例分析:德国银行信贷风险分析
查看>>
ant读书之使用ant进行java开发--第二章
查看>>