java编程之方法模板模型

java编程之方法模板模型

在日常开发过程中,难免遇到相对独立的功能模块的开发,如 监控告警 功能 ,
如何将此部分代码有效的管理起来,保证代码的可读性及易维护性是我们经常需要考虑的问题。 下面为大家介绍一种简单有效的编程模型-
方法模板模型,现以 监控告警 这个功能作为样例,为大家介绍‘方法模板模型’的使用。


  • 监控告警功能-方法模板关系模型Visio
  • 代码部分

方法模板关系模型Visio

这里写图片描述

代码部分

由于监控维度较多,以下只以AVG基线监控告警为例

/**
 * 监控告警任务上下文
 */
public class TaskContext {

    private AlarmConfig ac;

    public TaskContext(AlarmConfig alarmConfig){
        this.ac = alarmConfig;
    }

    public TaskContext(){}

    public boolean getResult(String message){
        return ac.warning(message);
    }

    public AlarmConfig getAc() {
        return ac;
    }

    public void setAc(AlarmConfig ac) {
        this.ac = ac;
    }
}

/**
 * 监控告警 配置 方法模板
 */
public interface AlarmConfig<T> {
    String getType();
    String getIntervalTime();
    String getConfigJson();
    boolean warning(String message);
}

/**
 * AVG基线监控配置类:
 * @author xiaoxiangxu
 *
 */
public class AvgBaseLineConfig implements AlarmConfig{
    private  String type = AlarmTypeEnum.AVG_BASELINE_LIMIT.getType();
    /** 下降百分比*/
    private Long threshold;
    private int numberOfTimes;
    //告警时间间隔
    private Integer warningIntervalTime;
    private String intervalTime;
    private String configJson;
    @Override
    public String getConfigJson() {
        return configJson;
    }

    public void setConfigJson(String configJson) {
        this.configJson = configJson;
    }

    @Override
    public String getType() {
        return type;
    }

    @Override
    public boolean warning(String message) {
        return false;
    }

    public AvgBaseLineConfig(LinkedTreeMap<String,Object> map ,QueryServiceImpl queryService) {
        List<EmergencyDefaultConfigModel> dec = queryService.queryAllEmergencyConfigModel();
        if(map!=null && map.size() > 0){
                NumberFormat formatter = new DecimalFormat("0");
                this.setThreshold(Long.parseLong(formatter.format(map.get("threshold"))));
                this.setNumberOfTimes(ParmsUtils.getSafeValue_Integer(formatter.format(map.get("numberOfTimes"))));
                this.setIntervalTime((String) map.get("intervalTime"));
                this.setConfigJson((String)map.get("configJson"));
        }
        if(dec!=null && dec.size() >0) {
            EmergencyDefaultConfigModel config = dec.get(0);
            if (!StringUtil.isEmpty(config.getDefultConfigJson())) {
                String jsonParm = config.getDefultConfigJson().trim();
                WarningDefaultConfigJsonBean depts = new Gson().fromJson(jsonParm, WarningDefaultConfigJsonBean.class);
                this.setWarningIntervalTime(depts.getAvgBaseLineConfig().getWarningIntervalTime());
            }
        }
    }
    public AvgBaseLineConfig(WarningDefaultConfigJsonBean.AvgBaseLineConfig_ t) {
        this.setConfigJson(t.getConfigJson());
        this.setIntervalTime(t.getIntervalTime());
        this.setWarningIntervalTime(t.getWarningIntervalTime());
        this.setThreshold(NumberUtil.toLong(String.valueOf(t.getThreshold())));
        this.setNumberOfTimes(t.getNumberOfTimes());
    }


    public void setType(String type) {
        this.type = type;
    }

    public long getThreshold() {
        return threshold;
    }

    public void setThreshold(long threshold) {
        this.threshold = threshold;
    }

    public int getNumberOfTimes() {
        return numberOfTimes;
    }

    public void setNumberOfTimes(int numberOfTimes) {
        this.numberOfTimes = numberOfTimes;
    }

    public Integer getWarningIntervalTime() {
        return warningIntervalTime;
    }

    public void setWarningIntervalTime(Integer warningIntervalTime) {
        this.warningIntervalTime = warningIntervalTime;
    }
    @Override
    public String getIntervalTime() {
        return intervalTime;
    }

    public void setIntervalTime(String intervalTime) {
        this.intervalTime = intervalTime;
    }

    public AvgBaseLineConfig() {}

}



/**
 * Created by xiaoxiangxu on 2015/12/14.
 */
@Component
public abstract class AlarmTaskTemplate {
    private static final Logger LOG = new Logger(AlarmTaskTemplate.class);
    public static final int DEFAULT_BATCH_SIZE = 1;//默认一个批次数量
    private String CQL_RESULT_QUERY = "select b_content from test.baseline_result where b_id='_KEY'";
    @Resource
    protected CacheDomain cacheDomain;
    @Resource
    protected SearchDomain searchDomain;
    @Resource
    protected CacheClient cacheClient;
    @Resource
    protected QueryServiceImpl queryService;

    protected ReadWriteLock lock = new ReentrantReadWriteLock(false);

    private String warningKey = "";

    private AlarmTaskModel tm ;

    private Long startTime;

    private Long endTime;

    private List<EmergencyDefaultConfigModel> dec;


    /**
     * 过滤满足条件的监控数据
     * @param taskModelList
     * @param interval
     */
    protected abstract boolean toWarning(List<AlarmTaskModel> taskModelList,String interval);


    /**
     * 告警信息
     * @param
     * @param config
     * @param yearMonthIndex
     * @param searchModel
     * @return
     */
    protected abstract String getAlarmMessage(Object config, String yearMonthIndex, SearchModel searchModel) ;


    /**
     * 获取基线key
     * @param
     * @param key
     * @return
     */
    protected abstract String getBaselineKey(String key) ;

    /**
     * 开始执行JOB
     * @param scheduleContext
     * @return
     * @throws Exception
     */
    public String getIntervalTime(ScheduleContext scheduleContext) throws Exception {
        return scheduleContext.getTaskGetResponse().getParameter().get("interval");
    }

    /**
     * 根据type获取默认告警时间间隔,默认1分钟
     * @param type
     * @return
     */
    public   int getDefaultIntervalTime(String type){
        if(dec==null || dec.size() ==0) {
            dec = queryService.queryAllEmergencyConfigModel();
        }
        try {
            Validate.notEmpty(dec, "warning : warningDefaultConfig has not config , please config default config as soon as possible !");
            EmergencyDefaultConfigModel config = dec.get(0);
            if (!StringUtil.isEmpty(config.getDefultConfigJson())) {
                WarningDefaultConfigJsonBean depts = new Gson().fromJson(config.getDefultConfigJson().trim(), WarningDefaultConfigJsonBean.class);
                if (type.equals(AlarmTypeEnum.FAILURES.getType())) {
                    return depts.getFailuresConfig().getWarningIntervalTime();
                } else if (type.equals(AlarmTypeEnum.AVG_BASELINE_LIMIT.getType())) {
                    return depts.getAvgBaseLineConfig().getWarningIntervalTime();
                } else if (type.equals(AlarmTypeEnum.AVG_TIME.getType())) {
                    return depts.getAvgTimeConfig().getWarningIntervalTime();
                } else if (type.equals(AlarmTypeEnum.LOWERLIMIT_MONITOR.getType())) {
                    return depts.getLowerLimitConfig().getWarningIntervalTime();
                } else if (type.equals(AlarmTypeEnum.RET_CODE_MONITOR.getType())) {
                    return depts.getRetCodeConfig().getWarningIntervalTime();
                } else if (type.equals(AlarmTypeEnum.TIME_OUT.getType())) {
                    return depts.getTimeOutConfig().getWarningIntervalTime();
                } else if (type.equals(AlarmTypeEnum.TP_MONITOR.getType())) {
                    return depts.getTpConfig().getWarningIntervalTime();
                } else if (type.equals(AlarmTypeEnum.TPS_BASELINE_LIMIT.getType())) {
                    return depts.getTpsBaseLineConfig().getWarningIntervalTime();
                }
            }
        }catch (Exception e){
            LOG.error(e.getMessage());
            return 1;
        }
        return 1;
    }
    /**
     * ES 查询条件
     * @param att
     * @return
     */
    protected SearchModel searchModel(AlarmTaskTemplate att){
        try {
            SearchModel searchModel = new SearchModel();
            if (att != null) {
                searchModel.setStartTime(att.getStartTime());
                searchModel.setEndTime(att.getEndTime());
                searchModel.setAppName(att.getTm().getAppName());
                searchModel.setServiceName(att.getTm().getServiceName());
                searchModel.setMethodName(att.getTm().getMethodName());
            }
            return searchModel;
        }catch (Exception e){
            LOG.error("CacheDomain saveWarningInterval() error : ", e);
            return null;
        }

    }



    /**
     * 获取配制的监控数据列表
     * @return
     */
    protected List<AlarmTaskModel> getTaskModelList(String interval){
        // 获取设置的参数(json格式)
        List<AlarmTaskModel> taskModelList= null;
        //如 缓存中间隔时间列表为空,则查全部监控数据,否则按间隔时间查询
        if(isEmpty(interval)) {
            taskModelList = searchDomain.queryAllAlarmTaskModel();
        }else {
            AlarmTaskModel alarmTaskModel = new AlarmTaskModel();
            alarmTaskModel.setCronExpression(interval);
            taskModelList = searchDomain.queryAlarmTaskModelByInterval(alarmTaskModel);
        }
        return taskModelList==null?new ArrayList<AlarmTaskModel>():setOtherInfo(taskModelList);
    }


    /**
     * 按KEY获取应用基线数据
     * @return
     */
    public  Map<String,List<BaselinePointModel>> getBaseLineByKey(String key){
        LOG.info("按KEY获取应用基线数据" ,key);
        Validate.notEmpty(key, "key is empty");
        return  searchDomain.searchBaselineByKey(key, CQL_RESULT_QUERY.replace("_KEY",getBaselineKey(key)));
    }

    /**
     * 增加告警邮箱与手机号
     * @param taskModelList
     * @return
     */
    protected List<AlarmTaskModel>  setOtherInfo(List<AlarmTaskModel> taskModelList){
       DepartmentOrApp departmentOrApp = null;
        try {
            for (AlarmTaskModel alarmTaskModel : taskModelList) {
                Set<String> alarmMobiles = new HashSet<String>();
                Set<String> alarmEmails = new HashSet<String>();
                if (alarmTaskModel == null || StringUtil.isEmpty(alarmTaskModel.getAppName())) {
                    continue;
                }
                alarmMobiles = StringUtil.isEmpty(alarmTaskModel.getMobiles())?new HashSet<String>():new HashSet<String>(Arrays.asList(alarmTaskModel.getMobiles().split(",")));
                departmentOrApp = searchDomain.getAppCenterInfo(alarmTaskModel.getAppName());
                if (departmentOrApp == null || departmentOrApp.getUser() == null) {
                    continue;
                }
                //来源:appCenter
                for (AlamUser alamUser : departmentOrApp.getUser()) {
                    alarmMobiles.add(alamUser.getPhone());
                    alarmEmails.add(alamUser.getMail());
                }
                //来源:默认配置
                for(String defaultEmailAddress : Constant.BASELINE_DEFAULT_EMAIL_ADDRESS.split(",")){
                    alarmEmails.add(defaultEmailAddress);
                }
                alarmTaskModel.setMobiles(new Gson().toJson(alarmMobiles));
                alarmTaskModel.setEmails(new Gson().toJson(getWarningEmailAddressByMobileFromCache(alarmMobiles,alarmEmails)));
            }
        }catch (Exception e){
            LOG.error("setOtherInfo error : ",e);
        }
        return taskModelList;
    }


    /**
     * 通过告警手机号,查询缓存中对应的email地址
     * @param alarmMobiles
     * @param alarmEmails
     */
   public Set<String> getWarningEmailAddressByMobileFromCache(Set<String> alarmMobiles,Set<String> alarmEmails){
       if(alarmMobiles==null){
           return alarmMobiles;
       }
       try{
           Map<String,LdapUserModel>  userModelMap = (Map<String,LdapUserModel>) cacheClient.getObjcet(Constant.LDAP_USER_INFO_CACHE);
           if(userModelMap!=null){
               Set<Map.Entry<String, LdapUserModel>> set = userModelMap.entrySet();
               for(Map.Entry<String, LdapUserModel> entry : set){
                   alarmEmails.add(entry.getValue().getMail());
               }
           }
       }catch (Exception e){
           LOG.error("getWarningEmailAddressByMobileFromCache error :",e);
       }
       return alarmEmails;
   }
    /**
     * 获取当前数据
     * @param app
     */
    protected Map<String, Integer> getPonitFromCassandraByKey(String type,String app,AlarmTaskTemplate template){
        Map<String, Integer> map = new HashMap<String, Integer>();
        //获取上次执行AlarmTask的时间戳,用于cassandra查询条件
        try {
            //查询cassandra AVG表数据
            List<List<Map<String, Object>>> result = searchDomain.getAppDataForWarning(app,type, cacheClient.getData(template.getWarningKey()), TimeUtils.getNowDay());
            if (result == null || result.size() == 0) {
                return map;
            }
            for (int i = 0; i < result.size(); i++) {
                List<Map<String, Object>> eachAppDateList = result.get(i);
                if(eachAppDateList==null || eachAppDateList.size()== 0){
                    continue;
                }
                for (int j = 0; j < eachAppDateList.size(); j++) {
                    map.put(TimeUtils.dateFormatForHH_MM_SS((Long) eachAppDateList.get(j).get("t")), (Integer) eachAppDateList.get(j).get("_"));
                }
            }
        } catch (Exception e) {
            LOG.error(e.getMessage(), e);
            return null;
        }
        return map;
    }

    public AlarmTaskTemplate() {}

    public AlarmTaskTemplate(String warningKey, AlarmTaskModel tm, Long startTime, Long endTime) {
        lock.writeLock().lock();
        this.warningKey = warningKey;
        this.tm = tm;
        this.startTime = startTime;
        this.endTime = endTime;
        lock.writeLock().unlock();
    }

    public SearchDomain getSearchDomain() {
        return searchDomain;
    }

    public void setSearchDomain(SearchDomain searchDomain) {
        this.searchDomain = searchDomain;
    }

    public CacheDomain getCacheDomain() {
        return cacheDomain;
    }

    public void setCacheDomain(CacheDomain cacheDomain) {
        this.cacheDomain = cacheDomain;
    }

    public CacheClient getCacheClient() {
        return cacheClient;
    }

    public void setCacheClient(CacheClient cacheClient) {
        this.cacheClient = cacheClient;
    }

    public QueryServiceImpl getQueryService() {
        return queryService;
    }

    public void setQueryService(QueryServiceImpl queryService) {
        this.queryService = queryService;
    }

    public String getWarningKey() {
        return warningKey;
    }

    public void setWarningKey(String warningKey) {
        this.warningKey = warningKey;
    }

    public AlarmTaskModel getTm() {
        return tm;
    }

    public void setTm(AlarmTaskModel tm) {
        this.tm = tm;
    }

    public Long getStartTime() {
        return startTime;
    }

    public void setStartTime(Long startTime) {
        this.startTime = startTime;
    }

    public Long getEndTime() {
        return endTime;
    }

    public void setEndTime(Long endTime) {
        this.endTime = endTime;
    }
}


/**
 * 报警任务 - 平均响应时间
 *
 * @author
 */

@Component
public class AVGBaselineAlarmTask extends AlarmJobTemplate implements SchedulerJob {
    private static final Logger LOG = new Logger(AVGBaselineAlarmTask.class);
    private AVGBaselineAlarmTask avg;
    @Override
    public void doJob(ScheduleContext scheduleContext) throws Exception {
        //告警入口
        long startTime = System.currentTimeMillis();
        LOG.info("# AVGBaselineAlarmTask do job start ");
        String interval = getIntervalTime(scheduleContext);
        toWarning(getTaskModelList(interval), interval);
        LOG.info("# AVGBaselineAlarmTask do job end, use ", System.currentTimeMillis() - startTime, " ms");
    }
    /**
     * 过滤满足条件的监控数据
     * @param taskModelList
     * @param interval
     */
    @Override
    protected boolean toWarning(List<AlarmTaskModel> taskModelList,String interval){
        if(taskModelList==null || taskModelList.size()==0){
            return false;
        }
        LOG.info("Alarm step2: taskModelList.size(): ",taskModelList.size(),",interval :",interval);
        AvgBaseLineConfig ac;
        try{
            for (AlarmTaskModel taskModel : taskModelList) {
                //解析监控规则 JSON 串
                if (taskModel.getType().contains(AlarmTypeEnum.AVG_BASELINE_LIMIT.getType())) {
                    //获取查询时间区间
                    Map<String, Long> map = TimeUtils.getTimeRangesAddOffsetTime(IntervalTimeEnum.getTimeUnitByTimeFlag(interval), Constant.getOFFSETS_TIME());
                    //告警任务:告警关键字,告警对象,开始时间,结束时间


**avg =new AVGBaselineAlarmTask(taskModel.getType() + "-" + taskModel.getAppName() + "-" + taskModel.getServiceName() + "-" + taskModel.getMethodName() + "-" + taskModel.getType(),taskModel,map.get("startTime"), map.get("endTime") );
                    //告警参数配置
                    //ac = new AvgBaseLineConfig(AlarmUtil.findAlarmConfigFromList(AlarmTypeEnum.AVG_BASELINE_LIMIT.getType(), new Gson().fromJson(taskModel.getRuleParameters(), List.class)),queryService){
                    ac = new AvgBaseLineConfig( new Gson().fromJson(taskModel.getRuleParameters(), WarningDefaultConfigJsonBean.AvgBaseLineConfig_.class)){
                       //告警入口
                        @Override
                        public boolean warning(String message) {
                            //告警方法:缓存对象,告警内容,告警任务模板对象,告警间隔时间
                            return AlarmUtil.sendAlarmMessage(cacheDomain,message,avg,getDefaultIntervalTime(AlarmTypeEnum.AVG_BASELINE_LIMIT.getType()));**

                        }
                    };
                    //通过告警上下文触发告警动作
                    return new TaskContext(ac).getResult(getAlarmMessage(ac, "", this.searchModel(avg)));
                }
            }
        }catch (Exception e){
            LOG.info("AvgBaseLineConfig run error ",e);
        }finally {
            if( !StringUtil.isEmpty(interval)){
                //缓存执行规则interval,作为对应用监控类型下拉选项
                cacheDomain.saveWarningInterval(AlarmTypeEnum.AVG_BASELINE_LIMIT.getType(),interval);
            }
        }
        return false;
    }

    /**
     * @param searchModel
     * @return
     * @description avg基线报警
     */
    @Override
    protected String getAlarmMessage(Object ac,String yearMonthIndex ,SearchModel searchModel) {
            Validate.notNull(ac, "warning object - ac  is null");
            String message = "";
            AvgBaseLineConfig c = (AvgBaseLineConfig)ac;
            try {
                String key = searchModel.getAppName();
                if(!StringUtil.isEmpty(searchModel.getServiceName())){
                    key+=":"+searchModel.getServiceName();
                    if(!StringUtil.isEmpty(searchModel.getMethodName())){
                        key+=":"+searchModel.getMethodName();
                    }
                }
                //通过比对基线与当前值进行比较获取告警信息
                message = this.getWarningMessage(getBaseLineByKey(key),key,c,this.getPonitFromCassandraByKey(Constant.AVG_BASE_LINE,key,avg));
            } catch (Exception e) {
                LOG.error(e.getMessage(), e);
            }
        return isEmpty(message)?"": "【" + message + "】";
    }

    @Override
    protected String getBaselineKey(String key) {
        return Constant.AVG_BASE_LINE+key+TimeUtils.getBeforeDateForDay(1);
    }




    /**
     * 根据基线值获取告警信息
     * @param key_
     * @param baselineMaps 基线值
     * @param currentMap 当前值
     * @param c 告警值
     * @return
     */
    public String getWarningMessage(Map<String,List<BaselinePointModel>> baselineMaps,String key_,AvgBaseLineConfig c,Map<String, Integer> currentMap){
        if(baselineMaps == null || baselineMaps.size() ==0 || currentMap==null || currentMap.size() ==0){
            return "";
        }
        LOG.info("Alarm step3: AVGBaselineAlarmTask getWarningMessage currentKey:",key_,",baseline.size():",baselineMaps.size(),",currentMap.size():"+currentMap.size());
        List<BaselinePointModel> baselineList  = baselineMaps.get(key_);
        if(baselineList == null || baselineList.size() == 0){
            return "";
        }
        int times_ = 0 ;
        int total = 0;
        for(BaselinePointModel baselinePointModel : baselineList){
            if(!currentMap.containsKey(baselinePointModel.getK())){
                continue;
            }
            int temp = currentMap.get(baselinePointModel.getK()) - baselinePointModel.getV();
            //如果平均响应时间大于基线
            if(temp > 0 && temp / Double.valueOf(baselinePointModel.getV()) * 100 > NumberUtil.toInt(String.valueOf(c.getThreshold()), DEFAULT_BATCH_SIZE)){
                times_ ++;
            }
            total++;
        }
        if(times_ >= c.getNumberOfTimes() && total>0){
            return  "AVG基线告警:KEY名称为:" + key_  + ",平均响应时间突破基线阈值:" + c.getThreshold() +  ",超出阈值:" + times_ + "次,占比为" +
                    new DecimalFormat("######0.00").format((times_ / NumberUtils.toDouble(String.valueOf(total))) * 100) + "%";
        }
        return "";
    }

    /**
     * 求AVG 占比
     * @param
     * @param key
     * @param value
     * @return
     */
    private long getBaseLinePointValue(BaselinePointModel baselinePointModel,String key,String value){
        try {
            Long totalValue = ParmsUtils.getSafeValue_Long(String.valueOf(baselinePointModel.getV()));
            Integer pointValue = ParmsUtils.getSafeValue_Integer(value);
            return totalValue - pointValue;
        }catch (Exception e){
            return 0L;
        }
    }
    /**
     * 获取pointMap key
     * @param key_
     * @return
     */
    private String getPonitKey(Object key_){
        return ParmsUtils.getSafeValue_String(key_);
    }
    /**
     * 求占比
     * @param numerator
     * @param denominator
     * @return
     */
    private int getPercent(int numerator,int denominator){
        NumberFormat formatter = new DecimalFormat("0");
        Double quotient=new Double(Double.valueOf(numerator)/Double.valueOf(denominator));
        return ParmsUtils.getSafeValue_Integer(formatter.format(quotient));
    }
    public AVGBaselineAlarmTask() {
        super();
    }

    public AVGBaselineAlarmTask(String warningKey, AlarmTaskModel tm, Long startTime, Long endTime) {
        super(warningKey, tm, startTime, endTime);
    }
}

到此 方法模板模型 样例介绍结束,代码量比较大未做过多删减,希望大家能够感受其中精妙之处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值