当前位置:   article > 正文

SPRING BOOT 动态定时任务_springboot 动态定时任务

springboot 动态定时任务

0.前言

传统spring定时任务采用的是@Sechedu注解去实现,但是该注解只能指定固定的时间任务,例如:配置了2s执行一次,那么永远只能是每两秒执行一次

但是在有些特殊场景下需要一些动态定时任务,例如:最初配置了2s执行一次,在执行任务中,修改配置为5秒执行一次,那么就需要动态的加载配置,使任务动态的变成5s执行一次

1.原理

要想实现动态定时任务,就需要借助SpringSchedulingConfigurer接口,该方式可以实现动态时间,定时任务,具体原理如下图:

2.Cron设计

如下图所示

定时任务数据库表设计

3. 配置解释

3.1 SchedulingConfigurer

要想实现定时任务主要是实现SchedulingConfigurer接口,然后重写configureTasks方法

3.2 ScheduledTaskRegistrar

configureTasks方法中,ScheduledTaskRegistrar通过addTriggerTask来添加触发器任务,从而去执行。而addTriggerTask方法有两个参数,一个是要执行得任务,一个是触发器

 具体配置代码如下:

  1. package net.test.api.job;
  2. import io.lettuce.core.dynamic.support.ReflectionUtils;
  3. import java.lang.reflect.Method;
  4. import java.util.Date;
  5. import java.util.List;
  6. import net.test.demo.common.framework.SpringContext;
  7. import net.test.dao.entity.demo.ScheduleSetting;
  8. import net.test.dao.mapper.demo.ScheduledSettingMapper;
  9. import org.apache.commons.collections4.CollectionUtils;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.scheduling.Trigger;
  12. import org.springframework.scheduling.TriggerContext;
  13. import org.springframework.scheduling.annotation.SchedulingConfigurer;
  14. import org.springframework.scheduling.config.ScheduledTaskRegistrar;
  15. import org.springframework.scheduling.support.CronTrigger;
  16. import org.springframework.stereotype.Component;
  17. @Component
  18. public class CronSchedule implements SchedulingConfigurer {
  19. @Autowired
  20. private ScheduledSettingMapper scheduledSettingMapper;
  21. @Override
  22. public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
  23. // 获取所有任务
  24. List<ScheduleSetting> scheduleList = scheduledSettingMapper.getScheduleList();
  25. System.out.println(scheduleList.size());
  26. if (CollectionUtils.isNotEmpty(scheduleList)){
  27. for (ScheduleSetting s : scheduleList){
  28. scheduledTaskRegistrar.addTriggerTask(getRunnable(s), getTrigger(s));
  29. }
  30. }
  31. }
  32. /**
  33. * 转换首字母小写
  34. *
  35. * @param str
  36. * @return
  37. */
  38. public static String lowerFirstCapse(String str) {
  39. char[] chars = str.toCharArray();
  40. chars[0] += 32;
  41. return String.valueOf(chars);
  42. }
  43. private Runnable getRunnable(ScheduleSetting scheduleSetting){
  44. return new Runnable() {
  45. @Override
  46. public void run() {
  47. Class<?> clazz;
  48. try {
  49. clazz = Class.forName(scheduleSetting.getBeanName());
  50. String className = lowerFirstCapse(clazz.getSimpleName());
  51. Object bean = SpringContext.getBean(className);
  52. Method method = ReflectionUtils.findMethod(bean.getClass(), scheduleSetting.getMethodName());
  53. if (StringUtils.isNotEmpty(scheduleSetting.getMethodParams())) {
  54. ReflectionUtils.invokeMethod(method, bean, scheduleSetting.getMethodParams());
  55. } else {
  56. ReflectionUtils.invokeMethod(method, bean);
  57. }
  58. } catch (ClassNotFoundException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. };
  63. }
  64. private Trigger getTrigger(ScheduleSetting scheduleSetting){
  65. return new Trigger() {
  66. @Override
  67. public Date nextExecutionTime(TriggerContext triggerContext) {
  68. CronTrigger trigger = new CronTrigger(scheduleSetting.getCronExpression());
  69. Date nextExec = trigger.nextExecutionTime(triggerContext);
  70. return nextExec;
  71. }
  72. };
  73. }
  74. }

多服务环境下定时任务重复执行问题解决方案

在开发的过程中,经常会遇到需要使用定时器的问题,比如需要定时向任务表写任务。但是项目是部署到集群环境下的,如果不做处理,就会出现定时任务重复执行的问题。问题产生的原因:由于我们项目同时部署在多台集群机器上,因此到达指定的定时时间时,多台机器上的定时器可能会同时启动,造成重复数据或者程序异常等问题
该问题的解决方案可能有以下几种,仅供参考:

  • 一、指定机器执行包含定时器任务

该方案操作简单,但只适合临时解决以下,正常生产环境应该没人会采取该方案。

  • 二、数据库加锁

通过数据库的锁机制,来获取任务执行状态,先更新,后执行的原则,可以实现避免任务重新执行的目标。

  • 三、redis的过期机制和分布式锁

我们最终决定采用的方案。

  1. public void doSomething() {
  2. Boolean locked = false;
  3. try {
  4. // 获取锁
  5. lock = redisTemplate.opsForValue().setIfAbsent(AsyncExportConstants.LOCK_KEY, AsyncExportConstants.LOCK_VALUE, 30, TimeUnit.SECONDS);
  6. if (locked) {
  7. //todo 执行业务逻辑
  8. }
  9. } finally {
  10. // 释放锁
  11. if (locked) {
  12. redisTemplate.delete(AsyncExportConstants.LOCK_KEY);
  13. }
  14. }
  15. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/728120
推荐阅读
相关标签
  

闽ICP备14008679号