监控设计--监控信息添入excel附件,进行邮件通知

业务背景

一个虚拟货币系统,需要日常监控,例如每日的新增流量统计、交易流水统计、异常交易统计等。
每日统计一次以上信息,并将统计信息添加到excel表中,以邮件的形式进行发送。

业务需求

1.可以在监控使用者无感知的情况下,添加新的监控任务
2.监控信息以excel文件形式告知管理者

监控设计

1.首先看监控使用者(定时任务)如何触发所有的监控:
在这里插入图片描述

2.monitorExecutor的代码

/**
 * 监控任务链的执行器。外部想执行所有监控请调用{@link #execute()}方法
 * 使用案例:{@link com.cesgroup.coin.cron.SystemMonitorTask#executeMonitorTask}
 * createTime: 2019-04-19 14:45
 * @author zack
 */
@Slf4j
@Component
@ConditionalOnBean(CoinMonitor.class)
public class MonitorExecutor implements ApplicationContextAware, InitializingBean {

    private ApplicationContext applicationContext;
    private Map<String, CoinMonitor> coinMonitorMap;
    private volatile CoinMonitor endOfMonitorChain;

    @Override
    public void afterPropertiesSet() {
        this.coinMonitorMap = applicationContext.getBeansOfType(CoinMonitor.class);
        log.info("加载的CoinMonitor:{}",coinMonitorMap);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 执行监控任务链,并将监控信息汇总到一个数据Map,以进行和Excel模板附件的结合,最后生成邮件附件Resource
     * 注:easyPoi的模板和数据只能结合一次。所以每个监控的internalExecute()方法传递的是数据而不是生成的文件流。
     * @return 因为我们系统的监控信息都是以excel文件形式告知管理者,所以监控链的最终产物是文件
     */
    public FileSystemResource execute() {
        initMonitorChain();
        assert endOfMonitorChain != null;
        //执行所有监控,获得包含所有监控信息的excel附件
        return endOfMonitorChain.execute();
    }


    private void initMonitorChain() {
        if (endOfMonitorChain == null) {
            synchronized (this) {
                if (endOfMonitorChain == null) {
                    int index = 0;
                    CoinMonitor monitor = null;
                    for (CoinMonitor nextMonitor : coinMonitorMap.values()) {
                        if (index == 0) {
                            monitor = nextMonitor;
                        } else {
                            monitor = monitor.addMonitor(nextMonitor);
                        }
                        index++;
                    }
                    this.endOfMonitorChain = monitor;
                }
            }
        }
    }


}

3.所有监控的父类,CoinMonitor

/**
 * 所有监控类都必须继承此接口,并被纳入spring的bean管理。如此才能每天定时执行监控任务。
 * 监控执行入口:{@link MonitorExecutor#execute}
 * 监控类示例:{@link UserBalanceMonitor}
 * createTime: 2019-04-17 10:38
 * @author zack
 */
public abstract class CoinMonitor {

    protected Logger log = LoggerFactory.getLogger(CoinMonitor.class);

    private CoinMonitor lastMonitor;


    /**
     * 添加下一个需要执行的监控,以便汇总监控信息,统一将信息添加到邮件附件
     */
     CoinMonitor addMonitor(CoinMonitor nextMonitor) {
        nextMonitor.lastMonitor = this;
        return nextMonitor;
    }


    /**
     * 执行监控任务链,并将监控信息汇总到一个数据Map,以进行和Excel模板附件的结合,最后生成邮件附件Resource
     * 注:easyPoi的模板和数据只能结合一次。所以每个监控的internalExecute()方法传递的是数据而不是生成的文件流。
     * @return 因为我们系统的监控信息都是以excel文件形式告知管理者,所以监控链的最终产物是文件
     */
     FileSystemResource execute() {
        Map<Integer, Map<String, Object>> sheetsData = new HashMap<>();
        sheetsData = execute(sheetsData);
        return mergeDataIntoExcelTemplate(sheetsData);
    }


    /**
     * @param sheetsData 上一个监控产生的Excel附件所需的Data
     * @return excel监控附件所需数据,key是excel表单的sheet的num,从0开始
     */
    private Map<Integer, Map<String, Object>> execute(Map<Integer, Map<String, Object>> sheetsData) {
        sheetsData = addMonitorData(sheetsData);
        if (lastMonitor != null) {
            sheetsData = lastMonitor.execute(sheetsData);
        }
        return sheetsData;
    }


    /**
     * 将数据和excel模板相结合,生成最终的excel附件
     * @param sheetsData excel监控附件所需的全部数据,key是excel表单的sheet的num,从0开始
     * @return 邮件Excel附件Resource对象
     */
    private FileSystemResource mergeDataIntoExcelTemplate(Map<Integer, Map<String, Object>> sheetsData) {
        TemplateExportParams excelTemplate = new TemplateExportParams(
                Constant.MONOTOR_EXCEL_TEMPLATE_CLASSPATH,true);
        Map<Integer, Map<String, Object>> defaultSheetsData = addDataToEverySheet(excelTemplate,sheetsData);
        try (FileOutputStream fos =
                     new FileOutputStream(Constant.MONITOR_TEMP_DATA_PATH)) {
            Workbook workbook = MutiSheetExcelExportUtil.exportExcel(defaultSheetsData, excelTemplate);
            workbook.write(fos);
        } catch (Exception e) {
            log.error("导出Excel监控数据异常:{}",e);
        }
        return new FileSystemResource(Constant.MONITOR_TEMP_DATA_PATH);
    }


    /**
     * 实现为excel的每个sheet页添加空的数据,避免某个monitor因为异常没有对相应sheet页提供数据,造成全部监控信息的丢失
     * @param excelTemplate 监控信息的excel模板
     * @param sheetsData 所有监控器产生的监控信息
     * @return 就算某个sheet页没有信息,也为其赋予空map
     */
    @SuppressWarnings("unchecked")
    private Map<Integer, Map<String, Object>> addDataToEverySheet(TemplateExportParams excelTemplate,
               Map<Integer, Map<String, Object>> sheetsData) {
        Workbook wb = ExcelCache.getWorkbook(excelTemplate.getTemplateUrl(), excelTemplate.getSheetNum(),
                excelTemplate.isScanAllsheet());
        int sheetNum = wb.getNumberOfSheets();
        Map<Integer, Map<String, Object>> defaultSheetsData = new HashMap<>();
        for (int i = 0;i < sheetNum;i++) {
            defaultSheetsData.put(i,new HashMap<>());
        }
        for (Map.Entry entry : sheetsData.entrySet()) {
            defaultSheetsData.put((Integer) entry.getKey(),(Map<String, Object>) entry.getValue());
        }
        return defaultSheetsData;
    }


    /**
     * 每个监控器需要执行的监控任务
     * 示例:{@link UserBalanceMonitor#addMonitorData}
     * @param sheetsData excel附件所需数据的集合,key是sheet的num,从0开始
     * @return 添加上本次监控信息的map数据集(key是sheet的num,value是对应sheet的数据)
     */
    protected abstract Map<Integer, Map<String, Object>> addMonitorData(Map<Integer, Map<String, Object>> sheetsData);

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值