quartz定时任务补录处理


前言

实现逻辑:定时器分业务定时器和补录定时器,业务定时器如果执行失败需要补录,调相关方法写入待补录信息,有补录定时器定时扫描补录。
支持设置补录次数。


一、Quartz是什么?

	Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:
	持久性作业 - 就是保持调度定时的状态;
	作业管理 - 对调度作业进行有效的管理;

二、使用步骤

1.补录采集器

ps:AbstractCollector为自封装调度器。

代码如下(示例):


/***  
 * <b>function:补录基础采集器(没有继承风险统计通用类)</b>
 */
public abstract class BaseSupplementCollectorWithoutStatiscs extends AbstractCollector {
    private static final Logger LOG = LoggerFactory.getLogger(BaseSupplementCollectorWithoutStatiscs.class);
    protected final static String DATE_FORMAT = "yyyyMMddHHmm";
    
    /**  
     * waitForRun:保存信息至缓存,等待补录定时器重新执行. <br/>  
     * @author chenpb
     * @param date
     * @param collectorFlag  
     */
    public void waitForRun(String dateStr, String collectorFlag) {
        NoCollectorData data = NoCollectorDataCacheManager.find(dateStr, collectorFlag);
        if(data == null) {
            data = new NoCollectorData(dateStr, collectorFlag);
            LOG.info("添加进缓存等待补录,{}", data.toString());
            NoCollectorDataCacheManager.getNoCollectorData().add(data);
        }
    }
    
    /**  
     * waitForRun:保存信息至缓存,等待补录定时器重新执行. <br/>  
     * @author chenpb
     * @param date
     * @param collectorFlag  
     */
    public void waitForRun(Date date, String collectorFlag) {
        String dateStr = DateUtil.dateToString(date, DATE_FORMAT);
        waitForRun(dateStr, collectorFlag);
    }
    
    /**  
     * checkIsSupplement:检查是否是补录,是则日期取补录数据的时次. <br/>  
     * @author chenpb
     * @param configuration
     * @param date
     * @return  
     */
    public Date checkIsSupplement(BaseNoCollectorDataConfiguration configuration, Date date) {
        LOG.info("是否补录:" + configuration.getFlag());
        if(configuration.getFlag()) {
            date = DateUtil.str2Date(configuration.getNoCollectorDate(), DATE_FORMAT);
        }
        return date;
    }
    
    /**  
     * checkRemove:当是补录时,删除对应缓存信息. <br/>  
     * @author chenpb
     * @param configuration
     * @param date
     * @param collectorFlag  
     */
    public void checkRemove(BaseNoCollectorDataConfiguration configuration, Date date, String collectorFlag) {
        String dateStr = DateUtil.date2Str(date, DATE_FORMAT);
        if(configuration.getFlag()) {
            NoCollectorDataCacheManager.remove(new NoCollectorData(dateStr, collectorFlag));
            LOG.info("{}的时次 {} 补录成功~~~~", collectorFlag, dateStr);
        }
    }
    
    public void lock(BaseNoCollectorDataConfiguration configuration) {
        configuration.setLocked(true);
    }
    
    public void unlock(BaseNoCollectorDataConfiguration configuration) {
        configuration.setLocked(false);
    }
}

2.业务定时器

继承补录采集器,重写collector定时任务主方法,关键代码:
(1)检查是否补录并加锁,加锁是为了防止该定时器在上一次调度没完成时重复执行

		//检查是否是补录,是日期取补录数据的时次
		date = checkIsSupplement(getRACSwanQPEConfiguration, date);
		lock(getRACSwanQPEConfiguration); //先加锁,防止在多次补录过程中同时进行,出现多个任务对同一份文件进行操作

(2)接口数据异常捕捉,记录待补录信息到缓存,等待补录定时器重写补录

		catch (CollectInterfaceException | NoCollectDataException e) {
            // 接口异常、接口无数据异常
            LOG.error(e.getMessage());
            unlock(getRACSwanQPEConfiguration);  //解锁
            //保存信息至缓存,等待另一个定时任务重新执行
            waitForRun(date, COLLECTOR_FLAG);
            //一定要返回,不进行下面的删除缓存操作
            return null;
		}

(3)调度完成后统一执行代码块,如果是补录,删除对应缓存信息,并解锁

		//当是补录时,没有出现接口异常、接口无数据异常,则删除对应缓存信息,使得不会再继续补录该时次了
        checkRemove(getRACSwanQPEConfiguration, date, COLLECTOR_FLAG);
        unlock(getRACSwanQPEConfiguration);  //解锁

代码如下(示例):

/**
 * 采集QPE数据<br />
 * 采集过去一小时、过去三小时、过去三小时的强降水
 */
public class GetRACSwanQPECollector extends BaseSupplementCollectorWithoutStatiscs {
    
	private static final Logger LOG = LoggerFactory.getLogger(GetRACSwanQPECollector.class);
	
	private final static String COLLECTOR_FLAG = "getRACSwanQPEConfiguration";//本采集器标识,对应collector-context.xml配置

	@Override
	public List<DataStore> collector(Configuration config) {
		LOG.info("{} collector execute.", config.getJobName());
		GetRACSwanQPEConfiguration getRACSwanQPEConfiguration = null;
		long start = System.currentTimeMillis();
		Date date = null;
		
		try {
			// 获取配置文件
			getRACSwanQPEConfiguration = (GetRACSwanQPEConfiguration) config;
			// 采集时间是否移动(可以加减分钟数)
			long moveMin = getRACSwanQPEConfiguration.getMoveMin();
			date = new Date(System.currentTimeMillis() + moveMin * 60 * 1000);
			
			//检查日期分钟是否为6的倍数,不是,向前取最近的
			date = checkDateTime(date);
			//检查是否是补录,是日期取补录数据的时次
            date = checkIsSupplement(getRACSwanQPEConfiguration, date);
            lock(getRACSwanQPEConfiguration); //先加锁,防止在多次补录过程中同时进行,出现多个任务对同一份文件进行操作
            
            // 业务逻辑
			collector(getRACSwanQPEConfiguration, date);
		} catch (ClassCastException e) {
            // 传入配置类型错误
            LOG.error("collector'configuration is error,{}采集器的配置类型应为{}",
                    config.getJobName(), config.getClass().getName());
        } catch (CollectInterfaceException | NoCollectDataException e) {
            // 接口异常、接口无数据异常
            LOG.error(e.getMessage());
            unlock(getRACSwanQPEConfiguration);  //解锁
            //保存信息至缓存,等待另一个定时任务重新执行
            waitForRun(date, COLLECTOR_FLAG);
            //一定要返回,不进行下面的删除缓存操作
            return null;
        } catch (Exception e) {
            LOG.info("系统出现未知异常,异常消息如下:{}", e);
        }
        //当是补录时,没有出现接口异常、接口无数据异常,则删除对应缓存信息,使得不会再继续补录该时次了
        checkRemove(getRACSwanQPEConfiguration, date, COLLECTOR_FLAG);
        LOG.info("【耗时】:{} 共耗时 :【{}】秒", this.getClass().getSimpleName(),
                (System.currentTimeMillis() - start) / 1000.0);
        unlock(getRACSwanQPEConfiguration);  //解锁
        return null;
	}

	@Override
	public DataStore dataMakeup(Configuration config, DataStore dataStore) {
		return null;
	}

}

xml配置

	<!-- QPE采集器 -->
    <bean id="getRACSwanQPECollector" class="com.linkcm.wmp.gd.collector.GetRACSwanQPECollector" />

	<!-- QPE采集配置 每6分钟执行 -->
    <bean id="getRACSwanQPEConfiguration" class="com.linkcm.wmp.gd.configure.GetRACSwanQPEConfiguration">
        <property name="cronExpression" value="0 0/6 * * * ?" />
        <property name="collector" ref="getRACSwanQPECollector" />
        <property name="url"
            value="采集接口" />
        <property name="dataFormat" value="yyyyMMddHHmm00" />
        <!-- 可选,采集时间偏移,用于数据源数据时间跟实际有偏差的情况,比如这里是每次采集当前时间-12分钟的时间数据-->
        <property name="moveMin" value="-12" />
        <!-- 可选,补录次数,不配置取补录采集器默认配置-->
        <!-- <property name="maxTimes" value="5"/> -->
    </bean>

3.补录定时器

业务定时器执行失败,需要补录时,写入缓存相关信息,由补录定时器定时扫描进行补录。

/***  
 * <b>function: 补录程序</b>
 */
public class SupplementCollector extends AbstractCollector {
    private static final Logger LOG = LoggerFactory.getLogger(SupplementCollector.class);

    /**  
     * 补录数据主方法.  
     * @see com.linkcm.collector.core.collector.ICollector#collector(com.linkcm.collector.core.configure.Configuration)  
     */
    @Override
    public List<DataStore> collector(Configuration configuration) {
        Set<NoCollectorData> set = NoCollectorDataCacheManager.getNoCollectorData();
        LOG.info("需要补录数据:{}", set.toString());
        
        synchronized(set) {
            Iterator<NoCollectorData> it = set.iterator();
            while(it.hasNext()) {
                final NoCollectorData data = it.next();
                if(data.getLocked()) {
                    LOG.info("为锁状态,需等待解锁才能进行补录!");
                    continue;
                }
                final CollectorFactory collectorFactory = (CollectorFactory) SpringContextUtil.getBean("collectorFactory");
                final Map<String, Configuration> configs = collectorFactory.getConfigurations();    //获取collector-context.xml配置的configurations属性
                
                LOG.info("开始补录数据,{}", data.toString());
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        BaseNoCollectorDataConfiguration config = (BaseNoCollectorDataConfiguration) configs.get(data.getConfiguration());   //获取对应Collector的配置信息
                        
                        //需要拷贝副本,不然会导致正常采集和补录采集的Configuration配置冲突
                        BaseNoCollectorDataConfiguration configCopy = config.clone();
                        SupplementConfiguration config2 = (SupplementConfiguration) configs.get("supplementConfiguration");   //获取补录采集器的配置信息
                        
                        Integer maxTimes = configCopy.getMaxTimes() != null ? configCopy.getMaxTimes() : config2.getMaxTimes();
                        if(data.getNumber() < maxTimes) { //控制补录次数
                            LOG.info("开始进行{}的第{}次补录", data.toString(), data.getNumber() + 1);
                            //更新该时次已执行次数+1
                            data.setNumber(data.getNumber() + 1);
                            NoCollectorDataCacheManager.addNoCollectorData(data);
                            
                            configCopy.setFlag(true);   //设置为补录状态
                            configCopy.setNoCollectorDate(data.getDataTime());   //设置补录数据的时次
                            Map<String, Object> collectorTypeMap = config2.getCollectorType();
                            
                            if(collectorTypeMap != null && collectorTypeMap.size() > 0) {
                                Object o = collectorTypeMap.get(data.getConfiguration());
                                if(o != null) {
                                    if(o instanceof BaseSupplementCollector) {
                                        BaseSupplementCollector collector = (BaseSupplementCollector) o;    //采集器类型-继承风险统计通用类
                                        collector.collector(configCopy);
                                    } else if(o instanceof BaseSupplementCollectorWithoutStatiscs) {
                                        BaseSupplementCollectorWithoutStatiscs collector = (BaseSupplementCollectorWithoutStatiscs) o;    //采集器类型-不继承风险统计通用类
                                        collector.collector(configCopy);
                                    } else {
                                        LOG.error("不支持的采集器类型!");
                                    }
                                } else {
                                    LOG.warn("补录采集器collectorType属性没有配置{}!", data.getConfiguration());
                                    data.setNumber(maxTimes);
                                }
                            }
                            LOG.info("进行{}的第{}次补录结束", data.toString(), data.getNumber());
                        } else {
                            LOG.info("已超过最大补录次数:{},该时次({})不再采集", maxTimes, data.toString());
                        }
                    }
                }).start();
                it.remove();
            }
        }
        return null;
    }

    @Override
    public DataStore dataMakeup(Configuration arg0, DataStore arg1) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void executeStore(Configuration arg0, DataStore arg1) {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // TODO Auto-generated method stub
        
    }
    
}

xml配置
collectorType配置需要补录的业务定时器

    <!-- 补录采集配置 -->
    <bean id="supplementConfiguration" class="com.linkcm.wmp.gd.configure.SupplementConfiguration">
        <property name="cronExpression" value="0 0/2 * * * ? 2099"/>
        <property name="collector" ref="supplementCollector"/>
        <!-- 默认补录次数 -->
        <property name="maxTimes" value="5"/>
        <!-- key对应业务定时器的COLLECTOR_FLAG标识 -->
        <property name="collectorType">
            <map>
                <entry key="getRACSwanQPEConfiguration" value-ref="getRACSwanQPECollector" />
            </map>
        </property>
    </bean>

总结

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值