監控設計--監控信息添入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);

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章