Spring集成quartz实现的定时任务调用

3 篇文章 0 订阅
2 篇文章 0 订阅

     本示例使用Spring+quartz实现定时任务的调度,通过zookeeper+curator实现分布式锁,保证分布式任务串行运行,通过自定义注解实现任务的扫描及注册;

1.添加相关的maven依赖,不包括spring

<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-client</artifactId>
	<version>2.8.0</version>
</dependency>
<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-framework</artifactId>
	<version>2.8.0</version>
</dependency>
<dependency>
	<groupId>org.apache.curator</groupId>
	<artifactId>curator-recipes</artifactId>
	<version>2.8.0</version>
</dependency>
<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.2.2</version>
</dependency>    

2.定义任务扫描的相关注解:

/**
 * 调度任务注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scheduler {

}
/**
 *  调度任务是否为安全的并行任务,默认为true,不允许并行
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScheduleTaskConcurrent {
    /**
     * 任务名称
     * @return
     */
    String  value();
}
/**
 * 任务表达式
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScheduleTaskCronExpression {
    /**
     * 任务名称
     * @return
     */
    String value();
}
/**
 * 任务方法
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScheduleTaskMethod {
    /**
     * 任务名称
     * @return
     */
    String value();
}
/**
 *  调度任务是否为分布式安全的并行任务,默认为true,不允许并行
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScheduleTaskRemoteConcurrent {
    /**
     * 任务名称
     * @return
     */
    String value();
}
/**
 * 任务是否执行
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScheduleTaskRunning {
    /**
     * 任务名称
     * @return
     */
    String value();
}



3.定义任务对象对应的实体bean:

/**
 * 任务对象
 */
public class TaskBean {

    private String name;
    private String cronExpression;
    private Boolean concurrent = true;
    private Boolean remoteConcurrent=true;
    private Boolean running = true;
    private Object targetObject;
    private String targetMethod;

    public TaskBean(String name, String cronExpression, Boolean running, Boolean concurrent, Boolean remoteConcurrent,Object targetObject, String targetMethod) {
        this.name = name;
        this.cronExpression = cronExpression;
        this.concurrent = concurrent;
        this.remoteConcurrent = remoteConcurrent;
        this.running = running;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
    }


    public TaskBean(String cronExpression, Object targetObject, String targetMethod) {
        this(null, cronExpression, null, null, targetObject, targetMethod);
    }

    public TaskBean(String name, String cronExpression, Object targetObject, String targetMethod) {
        this(name, cronExpression, null, null, targetObject, targetMethod);
    }

    public TaskBean(String cronExpression, Boolean running, Boolean stateful, Object targetObject, String targetMethod) {
        this(null, cronExpression, running, stateful, targetObject, targetMethod);
    }

    public TaskBean(String name, String cronExpression, Boolean running, Boolean concurrent, Object targetObject, String targetMethod) {
        this.name = StringUtils.isEmpty(name) ? getDefaultName() : name;
        this.cronExpression = cronExpression;
        this.running = running != null ? running : this.running;
        this.concurrent = concurrent != null ? concurrent : this.concurrent;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
    }

    public TaskBean(String name, String cronExpression, Boolean running, Boolean concurrent,
                           Object targetObject, String targetMethod,Boolean remoteConcurrent) {
        this.name = StringUtils.isEmpty(name) ? getDefaultName() : name;
        this.cronExpression = cronExpression;
        this.running = running != null ? running : this.running;
        this.concurrent = concurrent != null ? concurrent : this.concurrent;
        this.targetObject = targetObject;
        this.targetMethod = targetMethod;
        this.remoteConcurrent = remoteConcurrent != null ? remoteConcurrent : this.remoteConcurrent;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public Boolean getConcurrent() {
        return concurrent;
    }

    public void setConcurrent(Boolean concurrent) {
        this.concurrent = concurrent;
    }

    public Boolean getRemoteConcurrent() {
        return remoteConcurrent;
    }

    public void setRemoteConcurrent(Boolean remoteConcurrent) {
        this.remoteConcurrent = remoteConcurrent;
    }

    public Boolean getRunning() {
        return running;
    }

    public void setRunning(Boolean running) {
        this.running = running;
    }

    public Object getTargetObject() {
        return targetObject;
    }

    public void setTargetObject(Object targetObject) {
        this.targetObject = targetObject;
    }

    public String getTargetMethod() {
        return targetMethod;
    }

    public void setTargetMethod(String targetMethod) {
        this.targetMethod = targetMethod;
    }

    private String getDefaultName() {
        return this.targetObject.getClass().getName();
    }

}

4.定义任务初始化工厂类及其实现:

/**
 * 任务初始化工厂类
 */
public interface ScheduleFactory {
    public void init(List<TaskBean> taskBeanList) throws Exception;
}

@Component("quartzSchedulerFactory")
public class QuartzScheduleFactory implements ScheduleFactory{

    @Autowired
    private Scheduler quartzScheduler;

    public void init(List<TaskBean> taskBeanList) throws Exception {
        if(taskBeanList ==null || taskBeanList.size()<=0 ){
            return;
        }
        for(TaskBean taskBean : taskBeanList){
            if (taskBean != null) {
                if (taskBean.getRunning()) {
                    QuartzSchedulerUtils.createScheduleJob(quartzScheduler, taskBean);
                }
            }
        }
    }

}

5.zk实现的分布式锁:

zk配置信息的加载

public class LocalZookeeperPropertiesLoader{

    private static void putConfig(Properties localProperties, Map.Entry entry) {
        if (entry.getKey().equals(ZookeeperConfigConstants.ZOOKEEPER_ADDRESS)) {
            localProperties.put(ZookeeperConfigConstants.ZOOKEEPER_ADDRESS, entry.getValue());
        }
    }

    /**
     * 从默认配置文件 config.properties 中得到ZooKeeper的配置信息
     * 其中 ZooKeeper 的地址由 (系统属性 优先于 环境变量 优先于 配置文件)指定
     * @return 配置信息
     */
    @Override
    public Properties load() {
        Properties localProperties = new Properties();
        try {
            InputStream resourceAsStream = LocalZookeeperPropertiesLoader.class.getClassLoader().getResourceAsStream
                    ("config" + ".properties");
            localProperties.load(resourceAsStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        System.getenv().entrySet().forEach(entry -> putConfig(localProperties, entry));
        System.getProperties().entrySet().forEach(entry -> putConfig(localProperties, entry));

        return localProperties;
    }

    @Override
    public void destroy() {
    }

 初始化curatorFrameWork

public class CRMSCuratorFrameworkFactory {
    private CRMSCuratorFrameworkFactory() {
    }

    public static CuratorFramework createCuratorFramework(CuratorProperties configuration){
        return createCuratorFramework(configuration, null);
    }
    public static CuratorFramework createCuratorFramework(CuratorProperties curatorProperties, TreeCacheListener listener, String... paths){
        CuratorFramework curatorFramework = CuratorFrameworkFactory.builder().connectString(curatorProperties.getConnectString()).
                connectionTimeoutMs(curatorProperties.getConnectionTimeoutMs()).retryPolicy(curatorProperties.getRetryPolicy()).
                sessionTimeoutMs(curatorProperties.getSessionTimeoutMs()).build();
        curatorFramework.start();
        if(paths.length > 0 && listener != null){
            for(String path :paths){
                try {
                    if(curatorFramework.checkExists().forPath(path)==null){
                        curatorFramework.create().creatingParentsIfNeeded().forPath(path);
                    }
                    TreeCache watcher =TreeCache.newBuilder(curatorFramework,path).build();
                    watcher.getListenable().addListener(listener);
                    watcher.start();
                }catch (Exception e){
                    throw new RuntimeException(e);
                }
            }
        }
        return  curatorFramework;
    }
}

互斥锁获取后的回调

@FunctionalInterface
public interface MutexLockCallable {
    void call();
}

 分布式锁的实现

//zk实现的分布式互斥锁
public class ZookeeperMetuxLock {
    private static Logger log = LoggerFactory.getLogger(ZookeeperMetuxLock.class);
    /**
     * 分布式互斥锁
     * @param lockName 锁对应的zk节点的名称
     * @param time     阻塞时间
     * @param unit     阻塞时间单位
     * @param callback  获得锁需要执行的内容
     */
    public static boolean execute(String lockName,long time,TimeUnit unit, MetuxLockCallble callback){
        boolean acquired = false;
        CuratorProperties curatorProperties = CuratorPropertiesBuilder.getInstance().build(new LocalZookeeperPropertiesLoader().load());
        try (CuratorFramework curatorFramework = CRMSCuratorFrameworkFactory.createCuratorFramework(curatorProperties)) {
            //互斥分布式锁
            InterProcessMutex mutexLock = new InterProcessMutex(curatorFramework, lockName);
            if (mutexLock.acquire(time, unit)) {
                try {
                    callback.call();
                } finally {
                    mutexLock.release();
                    acquired = true;
                }
            }
        } catch (Exception ex) {
            log.error("MutexLock lock error! message:" + ex.getMessage(), ex);
        } finally {
            return acquired;
        }
    }

    /**
     * 分布式互斥锁
     *
     * @param lockName 锁对应的zookeeper节点名字
     * @param callback 获取锁后执行的内容
     * @return 是否成功获取锁
     */
    public static boolean execute(String lockName, MetuxLockCallble callback) {
        return execute(lockName, -1, null, callback);
    }
}

 6.quartz的job的实现:

public class QuartzJob implements Job {
    private static Logger log = LoggerFactory.getLogger(QuartzJob .class);

    private static final String MUTEX_PATH_ON_ZOOKEEPER_PREFIX = "/scheduler_lock/";

    public void execute(JobExecutionContext context) throws JobExecutionException {
        final TaskFactoryBean taskFactoryBean = (TaskFactoryBean) context.getMergedJobDataMap().get(QuartzSchedulerUtils.SCHEDULEFACTORYBEAN);
        if (taskFactoryBean.getRemoteConcurrent()) {
            String lockName = MUTEX_PATH_ON_ZOOKEEPER_PREFIX + taskFactoryBean.getName();
            ZookeeperMutexLock.execute(lockName, 0, TimeUnit.MICROSECONDS, () -> {
                QuartzJob .executeTask(taskFactoryBean);
            });
        } else {
            executeTask(taskFactoryBean);
        }
    }

    private static void executeTask(TaskFactoryBean taskFactoryBean) {
        try {
            Object targetObject = taskFactoryBean.getTargetObject();
            String targetMethod = taskFactoryBean.getTargetMethod();
            Method method = targetObject.getClass().getMethod(targetMethod, new Class[]{});
            if (method != null) {
                method.invoke(targetObject, new Object[]{});
            } else {
                log.error("task " + taskFactoryBean.getName() + " execute error! message: execute method is null");
            }
        } catch (Exception ex) {
            log.error("task " + taskFactoryBean.getName() + " execute error! message:" + ex.getMessage(), ex);
        }
    }

 7.创建job的工具类:

public class QuartzSchedulerUtils {
    public static final String SCHEDULEFACTORYBEAN = "scheduleFactoryBean";

    public static void createScheduleJob(Scheduler scheduler, TaskBean taskBean)throws SchedulerException {
        //同步或者异步
        Class<? extends Job> jobClass =  QuartzJob.class;

        //构建job信息
        String  jobName = taskBean.getName();
        String jobGroupName = jobName;

        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName,jobGroupName).build();

        //放入参数,运行时的方法可以获取
        jobDetail.getJobDataMap().put(SCHEDULEFACTORYBEAN,taskBean);

        //表达式调度构造器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(taskBean.getCronExpression());

        //按新的表达式构造一个新的trigger
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName,jobGroupName).withSchedule(scheduleBuilder).build();
        scheduler.scheduleJob(jobDetail,cronTrigger);
    }
}

8.定义调度管理的入口类:

@Component("schedulerManager")
@Lazy(false)
public class SchedulerManager implements ApplicationContextAware {
    private static Logger log = LoggerFactory.getLogger(SchedulerManager.class);
    private List<TaskFactoryBean> taskFactoryBeen = new ArrayList<TaskFactoryBean>();
    private SchedulerFactory schedulerFactory;

    @PostConstruct
    public void init() throws Exception {
        schedulerFactory.init(taskFactoryBeen);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        initTaskFactoryBeen(applicationContext);
        schedulerFactory = (SchedulerFactory) applicationContext.getBean("quartzSchedulerFactory");
    }

    private void initTaskFactoryBeen(ApplicationContext applicationContext) {
        Map<String, Object> taskBeanMap = applicationContext.getBeansWithAnnotation(Scheduler.class);
        if (taskBeanMap != null && taskBeanMap.size() > 0) {
            for (String beanName : taskBeanMap.keySet()) {
                Object taskBean = taskBeanMap.get(beanName);
                try {
                    List<TaskFactoryBean> currentTaskFactoryBeen = buildTaskFactoryBean(taskBean);
                    if (currentTaskFactoryBeen != null && currentTaskFactoryBeen.size() > 0) {
                        taskFactoryBeen.addAll(currentTaskFactoryBeen);
                    }
                } catch (Exception ex) {
                    log.error("Initializes the scheduling bean " + beanName + " failure ! message:" + ex.getMessage(), ex);
                    throw new RuntimeException(ex);
                }
            }
        }
    }

    private List<TaskFactoryBean> buildTaskFactoryBean(Object taskBean) throws Exception {
        Object targetBean = SpringTargetBeanUtils.getTarget(taskBean);
        Map<String, String> cronMap = new HashMap<String, String>();
        Map<String, Boolean> runningMap = new HashMap<String, Boolean>();
        Map<String, Boolean> concurrentMap = new HashMap<String, Boolean>();
        Map<String, Boolean> remoteConcurrentMap = new HashMap<String, Boolean>();
        Field[] fields = targetBean.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (field.isAnnotationPresent(ScheduleTaskCronExpression.class)) {
                ScheduleTaskCronExpression scheduleTaskCronExpression = field.getAnnotation(ScheduleTaskCronExpression.class);
                String taskName = scheduleTaskCronExpression.value();
                String cronExpression = (String) field.get(targetBean);
                cronMap.put(taskName, cronExpression);
            } else if (field.isAnnotationPresent(ScheduleTaskRunning.class)) {
                ScheduleTaskRunning scheduleTaskRunning = field.getAnnotation(ScheduleTaskRunning.class);
                String taskName = scheduleTaskRunning.value();
                Boolean running = (Boolean) field.get(targetBean);
                runningMap.put(taskName, running);
            } else if (field.isAnnotationPresent(ScheduleTaskConcurrent.class)) {
                ScheduleTaskConcurrent scheduleTaskConcurrent = field.getAnnotation(ScheduleTaskConcurrent.class);
                String taskName = scheduleTaskConcurrent.value();
                Boolean concurrent = (Boolean) field.get(targetBean);
                concurrentMap.put(taskName, concurrent);
            }else if (field.isAnnotationPresent(ScheduleTaskRemoteConcurrent.class)) {
                ScheduleTaskRemoteConcurrent scheduleTaskRemoteConcurrent = field.getAnnotation(ScheduleTaskRemoteConcurrent.class);
                String taskName = scheduleTaskRemoteConcurrent.value();
                Boolean concurrent = (Boolean) field.get(targetBean);
                remoteConcurrentMap.put(taskName, concurrent);
            }
        }

        List<TaskFactoryBean> currentTaskFactoryBeen = new ArrayList<TaskFactoryBean>();
        Method[] methods = targetBean.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(ScheduleTaskMethod.class)) {
                ScheduleTaskMethod scheduleTaskMethod = method.getAnnotation(ScheduleTaskMethod.class);
                String taskName = scheduleTaskMethod.value();
                String methodName = method.getName();
                String cronExpression = cronMap.get(taskName);
                Boolean running = runningMap.get(taskName);
                Boolean concurrent = concurrentMap.get(taskName);
                Boolean remoteConcurrent = remoteConcurrentMap.get(taskName);
                TaskFactoryBean taskFactoryBean = new TaskFactoryBean(taskName, cronExpression, running, concurrent, taskBean, methodName, remoteConcurrent);
                currentTaskFactoryBeen.add(taskFactoryBean);
            }
        }
        return currentTaskFactoryBeen;
    }

    public List<TaskFactoryBean> getTaskFactoryBeen() {
        return taskFactoryBeen;
    }

9.相关spring及zk的配置:

<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
	<!-- quartz 延时加载 -->
	<property name="startupDelay" value="1"/>
</bean>

config.properies

#zookeeper addresss
zk_address=ip:2181
#zk properties
zookeeper.config.sessionTimeoutMs=60000
zookeeper.config.connectionTimeoutMs=3000
zookeeper.config.retry.baseSleepTimeMs=100
zookeeper.config.retry.maxRetries=3

10. 创建test类:

@Component
@Scheduler
public class TestTask {
    private static final String ADDUSERTASK = "TestTask";

    @ScheduleTaskCronExpression(ADDUSERTASK)
    private String taskEx = "* * * * * ? *";

    @ScheduleTaskRunning(ADDUSERTASK)
    private Boolean taskRunning = true;

    @ScheduleTaskRemoteConcurrent(ADDUSERTASK)
    private Boolean remoteCon = true;


    @ScheduleTaskMethod(ADDUSERTASK)
    public void task() {
        System.out.println("this is the task");
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值