package com.tmall.pokemon.bulbasaur.schedule; import java.util.List; import java.util.Map; import java.util.Properties; import com.google.common.collect.Maps; import com.tmall.pokemon.bulbasaur.core.CoreModule; import com.tmall.pokemon.bulbasaur.core.Module; import com.tmall.pokemon.bulbasaur.persist.PersistHelper; import com.tmall.pokemon.bulbasaur.persist.PersistModule; import com.tmall.pokemon.bulbasaur.schedule.job.AnsyJob; import com.tmall.pokemon.bulbasaur.schedule.model.Delay; import com.tmall.pokemon.bulbasaur.schedule.model.Timer; import com.tmall.pokemon.bulbasaur.schedule.process.BulbasaurCleanerPrcessor; import com.tmall.pokemon.bulbasaur.schedule.process.BulbasaurJobCleanerProcessor; import com.tmall.pokemon.bulbasaur.schedule.process.BulbasaurJobProcessor; import com.tmall.pokemon.bulbasaur.schedule.quartz.MyDetailQuartzJobBean; import org.quartz.Trigger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.scheduling.quartz.CronTriggerFactoryBean; import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; /** * @author [email protected] * @Description: * @date 2012-12-6 下午1:16:23 */ public class ScheduleModule extends Module implements InitializingBean, BeanDefinitionRegistryPostProcessor, ApplicationListener<ContextRefreshedEvent> { private final static Logger logger = LoggerFactory.getLogger(ScheduleModule.class); public static ScheduleModule self = null; /** * 是否删除过期job,默认不删除 */ private Boolean deleteOverdueJob = false; /** * 自动清理process,防止过度膨胀 */ private List<String> autoCleanDefinitionNameList; public boolean getDeleteOverdueJob() { return deleteOverdueJob; } public void setDeleteOverdueJob(Boolean deleteOverdueJob) { this.deleteOverdueJob = deleteOverdueJob; } public List<String> getAutoCleanDefinitionNameList() { return autoCleanDefinitionNameList; } public void setAutoCleanDefinitionNameList(List<String> autoCleanDefinitionNameList) { this.autoCleanDefinitionNameList = autoCleanDefinitionNameList; } public ScheduleModule() { } public static ScheduleModule getInstance() { if (self == null) { self = new ScheduleModule(); } return self; } @Override public Module[] require() { return new Module[] {CoreModule.getInstance(), PersistModule.getInstance()}; } @Override public void afterInit(String ownSign, String quartzTablePrefix) { AnsyJob.initExecutor(); checkParam(); //初始化 Timer 节点,set到coreModule里面去 CoreModule.getInstance().setStateClasses(Timer.class, Delay.class); registerQuartz(ownSign, quartzTablePrefix); } /* * (non-Javadoc) * * @see * org.springframework.beans.factory.InitializingBean#afterPropertiesSet() */ @Override public void afterPropertiesSet() throws Exception { self = this; } private void checkParam() { // nothing } /** * 监听整个容器启动完成 ,整个容器启动完,外部上下文 和 bulbasaur 内部上下文的父子关系已经建立,才能子容器拿不到 拿父容器 * * @param event */ @Override public void onApplicationEvent(ContextRefreshedEvent event) { /** * 只有根容器初始化完成搞一把,其他子容器比如spring mvc 完成就不要重复处理了 */ if (event.getApplicationContext().getParent() == null) { logger.warn("整个容器初始化完成,与业务容器对接完成!"); } } /** * 因为存在group分组,避免不同应用使用bulbasuar 并使用相同库,而产生trigger 等相互覆盖 只有在整个容器初始化完成后,才有 xxxxx 再初始化quartz */ private void registerQuartz(String ownSign, String quartzTablePrefix) { registerJobDetails(ownSign); registerTriggers(ownSign); registerScheduler(ownSign, quartzTablePrefix); } private static String lowerFirst(String param) { char[] chars = param.toCharArray(); chars[0] += 32; return String.valueOf(chars); } private static String suffixDetail(String param) { return lowerFirst(param) + "Detail"; } private static String suffixTrigger(String param) { return lowerFirst(param) + "Trigger"; } public final static String BULBASAUR_JOB_EXECUTE = "execute"; private void registerJobDetails(String ownSign) { registerJobDetail(ownSign, lowerFirst(BulbasaurJobProcessor.class.getSimpleName()), BULBASAUR_JOB_EXECUTE, suffixDetail(BulbasaurJobProcessor.class.getSimpleName())); registerJobDetail(ownSign, lowerFirst(BulbasaurCleanerPrcessor.class.getSimpleName()), BULBASAUR_JOB_EXECUTE, suffixDetail(BulbasaurCleanerPrcessor.class.getSimpleName())); registerJobDetail(ownSign, lowerFirst(BulbasaurJobCleanerProcessor.class.getSimpleName()), BULBASAUR_JOB_EXECUTE, suffixDetail(BulbasaurJobCleanerProcessor.class.getSimpleName())); } private void registerTriggers(String ownSign) { registerTrigger(ownSign, "[bulbasaur]job不间断尝试执行", suffixDetail(BulbasaurJobProcessor.class.getSimpleName()), "0/30 * * * * ?", suffixTrigger(BulbasaurJobProcessor.class.getSimpleName())); registerTrigger(ownSign, "[bulbasaur]定时清理执行完的流程和节点", suffixDetail(BulbasaurCleanerPrcessor.class.getSimpleName()), "0 0 0/6 * * ?", suffixTrigger(BulbasaurCleanerPrcessor.class.getSimpleName())); registerTrigger(ownSign, "[bulbasaur]定时清理job", suffixDetail(BulbasaurJobCleanerProcessor.class.getSimpleName()), "0 0 0/2 * * ?", suffixTrigger(BulbasaurJobCleanerProcessor.class.getSimpleName())); } private void registerScheduler(String ownSign, String quartzTablePrefix) { BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition( SchedulerFactoryBean.class); prepare(beanDefinitionBuilder, quartzTablePrefix); org.quartz.Trigger[] triggers = { (Trigger)beanFactory.getBean(suffixTrigger(BulbasaurJobProcessor.class.getSimpleName())), (Trigger)beanFactory.getBean(suffixTrigger(BulbasaurCleanerPrcessor.class.getSimpleName())), (Trigger)beanFactory.getBean(suffixTrigger(BulbasaurJobCleanerProcessor.class.getSimpleName())), }; beanDefinitionBuilder.addPropertyValue("triggers", triggers); registry.registerBeanDefinition(ownSign + "BulbasaurScheduler", beanDefinitionBuilder.getRawBeanDefinition()); logger.warn("****** BulbasaurScheduler init over ******"); } protected void prepare(BeanDefinitionBuilder beanDefinitionBuilder, String quartzTablePrefix) { beanDefinitionBuilder.addPropertyValue("applicationContextSchedulerContextKey", "applicationContext"); //设置自动启动 beanDefinitionBuilder.addPropertyValue("autoStartup", true); //需要overwrite已经存在的job,如果需要动态的修改已经存在的job,就需要设置为true,否则会以数据库中已经存在的为准 beanDefinitionBuilder.addPropertyValue("overwriteExistingJobs", true); beanDefinitionBuilder.addPropertyValue("dataSource", PersistModule.getInstance().getDataSource()); beanDefinitionBuilder.addPropertyValue("transactionManager", PersistHelper.getTransactionManager()); Properties properties = new Properties(); // 设置表前缀 properties.setProperty("org.quartz.jobStore.tablePrefix", quartzTablePrefix); properties.setProperty("org.quartz.scheduler.instanceName", "bulbasaurSchedule"); properties.setProperty("org.quartz.scheduler.instanceId", "AUTO"); properties.setProperty("org.quartz.scheduler.skipUpdateCheck", "true"); //Subclass of Quartz's JobStoreCMT class properties.setProperty("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); //在给定的通行证中,工作区将处理的最大错误次数触发。一次处理很多(超过几十打)可能导致数据库表被锁定得足够长,以致可能会阻碍其他(未失败的)Triggers触发的性能。 properties.setProperty("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); //在被认为“misfired”之前,调度程序将“tolerate”一个Triggers将其下一个启动时间通过的毫秒数。默认值(如果您在配置中未输入此属性)为60000(60秒)。 properties.setProperty("org.quartz.jobStore.misfireThreshold", "60000"); //值“true”告诉Quartz 在JDBC连接上调用setTransactionIsolation(Connection // .TRANSACTION_SERIALIZABLE)。这可以有助于防止在高负载下的某些数据库的锁定超时以及“持久”事务。 properties.setProperty("org.quartz.jobStore.txIsolationLevelSerializable", "true"); //设置为“true”以打开群集功能。如果您有多个Quartz实例使用同一组数据库表,则此属性必须设置为“true”,否则您将遇到破坏。 properties.setProperty("org.quartz.jobStore.isClustered", "true"); //“使用属性”标志指示JDBCJobStore,JobDataMaps中的所有值都将是“字符串”,因此可以将其存储为名称 - // 值对,而不是以BLOB列的序列化形式存储更复杂的对象。这可以方便,因为您避免了将非String类序列化为BLOB时可能产生的类版本控制问题。 properties.setProperty("org.quartz.jobStore.useProperties", "false"); //设置此实例“检入”*与群集的其他实例的频率(以毫秒为单位)。影响检测失败实例的速度。 properties.setProperty("org.quartz.jobStore.clusterCheckinInterval", "20000"); //Configure ThreadPool properties.setProperty("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); properties.setProperty("org.quartz.threadPool.threadCount", "3"); properties.setProperty("org.quartz.threadPool.threadPriority", "5"); properties.setProperty("org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread", "true"); beanDefinitionBuilder.addPropertyValue("quartzProperties", properties); } private void registerTrigger( String ownSign, String name, String jobDetailBeanName, String cronExpression, String triggerName ) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( CronTriggerFactoryBean.class); builder.addPropertyValue("name", name); builder.addPropertyValue("group", ownSign); builder.addPropertyValue("jobDetail", beanFactory.getBean(jobDetailBeanName)); builder.addPropertyValue("cronExpression", cronExpression); registry.registerBeanDefinition(triggerName, builder.getRawBeanDefinition()); } private void registerJobDetail( String ownSign, String targetObject, String targetMethod, String jobDetailBeanName ) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition( JobDetailFactoryBean.class); builder.addPropertyValue("jobClass", MyDetailQuartzJobBean.class); builder.addPropertyValue("group", ownSign); //durability 表示任务完成之后是否依然保留到数据库,默认false builder.addPropertyValue("durability", "true"); Map<String, String> jobDataAsMap = Maps.newHashMap(); jobDataAsMap.put("targetObject", targetObject); jobDataAsMap.put("targetMethod", targetMethod); builder.addPropertyValue("jobDataAsMap", jobDataAsMap); registry.registerBeanDefinition(jobDetailBeanName, builder.getRawBeanDefinition()); } private ConfigurableListableBeanFactory beanFactory; private BeanDefinitionRegistry registry; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { this.registry = registry; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }