Spring Scheduled + Redis 实现分布式定时器(2)


原文:https://blog.csdn.net/wzs298/article/details/77716954


新版本将引入以下内容:
  • 加入Mysql做持久化管理;(作用:动态管理所有任务)
  • 内置心跳检测定时器;(作用:监听有变更的任务)
流程:
  1. 列表内容
  2. 启动项目实例;
  3. 加载所有的自定义注解和bean信息;
  4. 查询数据库目前存在的任务信息;
  5. 将新的自定义注解加入数据库中;
  6. 执行心跳检测定时器;
  7. 心跳检测数据库中的任务是否有变更;有:执行对应动作!
说明以下要用到的类和作用:
  • KyScheduledFuture: 用于记录当前正在运行的任务;
  • KyTaskDetails: 关联数据库ky_task_details表的实体类,记录所有载入的任务状态;
  • KyTriggerDetails: 关联数据库ky_trigger_details表的实体类,主要是记录定时规则;用于跟KyTaskDetails配对;
  • KyTrigger: 自定义触发器类;里面记录了cron、syncLock、获取下一次执行时间的信息;
  • KyScheduledExecution: 自定义注解的实现类;
  • KyScheduledController: 动态管理任务的控制类;
  • KyScheduled: 自定义注解;
  • HeartBeatJob:心跳任务;

**用户只需要控制ky_task_details和ky_trigger_details两张表即可。 心跳定时器会帮我们自动触发相应的动作; 
当然,如果不想使用心跳检测的方式,也可以用提供接口的方式。每次修改ky_task_details信息时,主动去调用一下接口。**

创建mysql表语句:

CREATE TABLE `ky_task_details` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `taskName` varchar(255) COLLATE utf8_bin NOT NULL,
  `beanName` varchar(255) COLLATE utf8_bin NOT NULL,
  `state` int(1) NOT NULL,
  `triggerId` int(11) unsigned zerofill NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
CREATE TABLE `ky_trigger_details` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8_bin NOT NULL,
  `cron` varchar(255) COLLATE utf8_bin NOT NULL,
  `state` varchar(255) COLLATE utf8_bin NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

接下来分解代码:(借鉴代码的时候可能要根据项目实际情况做些改动) 
KyScheduled:新增name和synclock属性;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface KyScheduled {
    /**
     * A cron-like expression, extending the usual UN*X definition to include
     * triggers on the second as well as minute, hour, day of month, month
     * and day of week.  e.g. {@code "0 * * * * MON-FRI"} means once per minute on
     * weekdays (at the top of the minute - the 0th second).
     * @return an expression that can be parsed to a cron schedule
     */
    String cron() default "";
    /**
     * 自定义的定时器任务名称,为空时使用对应的方法名作为任务名;
     * @return ""
     */
    String name() default "";
    /**
     * 是否使用同步锁, 默认:使用
     * @return
     */
    boolean synclock() default true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

KyScheduledFuture:

public class KyScheduledFuture {
    private String cron;
    private ScheduledFuture<?> future;
    public KyScheduledFuture(String cron, ScheduledFuture<?> future) {
        this.cron = cron;
        this.future = future;
    }
    public String getCron() {
        return cron;
    }
    public void setCron(String cron) {
        this.cron = cron;
    }
    public java.util.concurrent.ScheduledFuture<?> getFuture() {
        return future;
    }
    public void setFuture(java.util.concurrent.ScheduledFuture<?> future) {
        this.future = future;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

KyTaskDetails:与数据库ky_task_details表关联的实体类;

public class KyTaskDetails {
    private long id; //id
    private String taskName; //用户自定义的任务名
    private String beanName; //redis的lockName, 也是唯一值
    private KyTrigger trigger; //触发器
    private KyScheduledExecution.Job job; //任务
    private int state = 2; //当前任务是否正在执行。1:执行,2:未执行
    private long triggerId;//触发器id
    private KyTriggerDetails triggerDetails;
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public String getBeanName() {
        return beanName;
    }
    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
    }
    public long getTriggerId() {
        return triggerId;
    }
    public void setTriggerId(long triggerId) {
        this.triggerId = triggerId;
    }
    public KyTrigger getTrigger() {
        return trigger;
    }
    public void setTrigger(KyTrigger trigger) {
        this.trigger = trigger;
    }
    public KyScheduledExecution.Job getJob() {
        return job;
    }
    public void setJob(KyScheduledExecution.Job job) {
        this.job = job;
    }
    public KyTriggerDetails getTriggerDetails() {
        return triggerDetails;
    }
    public void setTriggerDetails(KyTriggerDetails triggerDetails) {
        this.triggerDetails = triggerDetails;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

KyTriggerDetails:与数据库ky_trigger_details表关联的实体类;

public class KyTriggerDetails {
    private long id;
    private String name;
    private String cron;
    private int state;
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getCron() {
        return cron;
    }
    public void setCron(String cron) {
        this.cron = cron;
    }
    public int getState() {
        return state;
    }
    public void setState(int state) {
        this.state = state;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

KyTrigger:自定义触发器,记录自定义注解的属性信息和提供一个获取下一次执行任务时间的方法;

/**
 * 自定义触发器对象
 */
public class KyTrigger implements Trigger, Serializable {
    private String cron;
    private boolean syncLock;
    public KyTrigger(KyScheduled kyScheduled){
        if(kyScheduled.cron() != null && !"".equals(kyScheduled.cron())) {
            this.cron = kyScheduled.cron();
        }
        this.syncLock = kyScheduled.synclock();
    }
    public boolean getSyncLock(){
        return  this.syncLock;
    }
    public String getCron() {
        return cron;
    }
    public void setCron(String cron) {
        if(cron != null && !"".equals(cron)) {
            this.cron = cron;
        }
    }
    @Override
    public Date nextExecutionTime(TriggerContext triggerContext) {
        CronTrigger cronTrigger = new CronTrigger(this.cron);
        return cronTrigger.nextExecutionTime(triggerContext);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

KyScheduledController:持久化任务的控制器, 主要是记录当前正在运行的任务,以及任务的启动、停止、变更;

@Component
@EnableScheduling
public class KyScheduledController {
    @Autowired
    private KyScheduledExecution scheduledExecution;
    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    private Map<String, KyScheduledFuture> futureMap = new HashMap<>();
    @Bean
    public ThreadPoolTaskScheduler KyScheduledController() {
        return new ThreadPoolTaskScheduler();
    }
    /**
     * 创建新定时器
     * @param kyTaskDetails KyTaskDetails
     * @return String
     */
    public String startTask(KyTaskDetails kyTaskDetails) {
        String beanName = kyTaskDetails.getBeanName();
        if (futureMap.get(beanName) == null) {
            KyTaskDetails kyTaskInfo = scheduledExecution.getTaskDetailMap().get(beanName);
            if(kyTaskInfo != null) {
                String cron = kyTaskInfo.getTrigger().getCron();
                ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(kyTaskInfo.getJob(), new CronTrigger(cron));
                futureMap.put(kyTaskInfo.getBeanName(), new KyScheduledFuture(cron, future));
            }
            return "startTask";
        }
        return "";
    }
    /**
     * 修改定时器
     * @param kyTaskDetails KyTaskDetails
     * @return String
     */
    public String changeTask(KyTaskDetails kyTaskDetails) {
        String beanName = kyTaskDetails.getBeanName();
        String cron = kyTaskDetails.getTrigger().getCron();
        stopTask(beanName);
        if (futureMap.get(beanName) == null) {
            KyTaskDetails kyTaskInfo = scheduledExecution.getTaskDetailMap().get(beanName);
            kyTaskInfo.getTrigger().setCron(cron);
            ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(kyTaskInfo.getJob(), new CronTrigger(kyTaskInfo.getTrigger().getCron()));
            futureMap.put(kyTaskInfo.getBeanName(), new KyScheduledFuture(cron, future));
            return "changeTask";
        }
        return "";
    }
    /**
     * 关闭定时器
     * @param name String
     * @return String
     */
    public String stopTask(@PathVariable("name") String name) {
        KyScheduledFuture kyFuture = futureMap.get(name);
        if (kyFuture != null) {
            kyFuture.getFuture().cancel(true);
            futureMap.remove(name);
            return "stopTask";
        }
        return "";
    }
    public Map<String, KyScheduledFuture> getFutureMap() {
        return futureMap;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

HeartBeatJob:心跳检测任务不计入持久化范围,主要是监听数据库的变化和执行定时器操作;

@Component
public class HeartBeatJob {
    @Autowired
    private KyScheduledController scheduledController;
    @Autowired
    private KyScheduledExecution scheduledExecution;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @KyScheduled(cron = "0/5 * * * * ?", name = "heartbeatCheck", synclock = false)
    public void heartbeatCheck() {
        Map<String, KyScheduledFuture> futureMap = scheduledController.getFutureMap();
        List<KyTaskDetails> dbTaskDetailsList = new ArrayList<>();
        List<Map<String, Object>> rows = jdbcTemplate.queryForList("select * from ky_task_details");
        Iterator taskIt = rows.iterator();
        while (taskIt.hasNext()) {
            Map map = (Map) taskIt.next();
            KyTaskDetails ktd = scheduledExecution.getTaskDetailMap().get(map.get("beanName").toString());
            if (ktd != null) {
                ktd.setId(Long.parseLong(map.get("id").toString()));
                ktd.setState(Integer.parseInt(map.get("state").toString()));
                ktd.setTriggerId(Long.parseLong(map.get("triggerId").toString()));
                dbTaskDetailsList.add(ktd);
            }
        }
        for (KyTaskDetails task : dbTaskDetailsList) {
            if (task.getTriggerId() != 0L) {
                Map<String, Object> map;
                try {
                    map = jdbcTemplate.queryForMap("select * from ky_trigger_details where id = " + task.getTriggerId());
                } catch (Exception ex) {
                    map = null;
                }
                if(map != null) {
                    KyTriggerDetails triggerDetails = new KyTriggerDetails();
                    triggerDetails.setId(Long.parseLong(map.get("id").toString()));
                    triggerDetails.setName(map.get("name").toString());
                    triggerDetails.setCron(map.get("cron").toString());
                    triggerDetails.setState(Integer.parseInt(map.get("state").toString()));
                    task.getTrigger().setCron(map.get("cron").toString());
                    task.setTriggerDetails(triggerDetails);
                }
            }
        }
        for (KyTaskDetails task : dbTaskDetailsList) {
            //1、判断任务状态
            KyScheduledFuture future = futureMap.get(task.getBeanName());
            if (future == null && task.getState() == 1) {//新增
                scheduledController.startTask(task);
            } else {
                int closeTask = 2;
                if (task.getState() == closeTask) {
                    scheduledController.stopTask(task.getBeanName());
                } else {
                    String dbTriggerCron = task.getTriggerDetails() != null ?task.getTriggerDetails().getCron() : "";
                    if (!dbTriggerCron.equals(future.getCron()) && !"".equals(dbTriggerCron)) {
                        scheduledController.changeTask(task);
                    }
                }
            }
        }
        //4、关闭定时器
        for (String beanName : futureMap.keySet()) {
            boolean isDel = true;
            for (KyTaskDetails task : dbTaskDetailsList) {
                if (task.getBeanName().equals(beanName)) {
                    isDel = false;
                    break;
                }
            }
            if (isDel) {
                scheduledController.stopTask(beanName);
            }
        }
        System.out.println(new Date() + ", 心跳检测....  ");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

KyScheduledExecution:主要更变在configureTasks方法中,加入了创建持久化对象;

@Component
public class KyScheduledExecution implements BeanPostProcessor, SchedulingConfigurer, ApplicationContextAware {
    private Log log = LogFactory.getLog(getClass());
    @Value("${spring.redis.host}")
    private String redisHost;
    @Value("${spring.redis.port}")
    private int redisPort;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    private Jedis jedis;
    private Map<String, KyTaskDetails> taskDetailMap = new HashMap<String, KyTaskDetails>();
    //记录任务集合
    private List<KyTask> kyTaskList = new ArrayList<>();
    private ApplicationContext applicationContext;
    public Map<String, KyTaskDetails> getTaskDetailMap() {
        return taskDetailMap;
    }
    /**
     * 创建redis客户端
     */
    private void createRedisClient() {
        if (jedis == null) {
            jedis = new Jedis(redisHost, redisPort);
        }
    }
    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        createRedisClient();
        this.applicationContext = context;
    }
    private Object getBean(Class classname) {
        try {
            return this.applicationContext.getBean(classname);
        } catch (Exception e) {
            log.error(e);
            return "";
        }
    }
    public class KyTask {
        private KyScheduled kyScheduled;
        private Method kyMethod;
        public KyScheduled getKyScheduled() {
            return kyScheduled;
        }
        public void setKyScheduled(KyScheduled kyScheduled) {
            this.kyScheduled = kyScheduled;
        }
        public Method getKyMethod() {
            return kyMethod;
        }
        public void setKyMethod(Method kyMethod) {
            this.kyMethod = kyMethod;
        }
    }
    /**
     * 任务对象
     */
    public class Job implements Serializable, Runnable {
        private Method method;
        private String lockName;
        private Object invokeMethod;
        private KyTrigger trigger;
        public String getLockName() {
            return lockName;
        }
        Job(Method m, KyTrigger t) {
            this.trigger = t;
            this.invokeMethod = getBean(m.getDeclaringClass());//获取bean实例
            this.lockName = m.getDeclaringClass().getName() + "." + m.getName();//构造LockName
            this.method = m;
        }
        @Override
        public void run() {
            synchronized (this) {
                if (this.trigger.getSyncLock()) {
                    //获取下次执行时间(秒)
                    long nextTime = (this.trigger.nextExecutionTime(new SimpleTriggerContext()).getTime() - new Date().getTime()) / 1000;
                    //抢占分布式锁
                    String result = setnxLock(this.lockName, (int) nextTime);
                    if (result != null && !"".equals(result)) {
                        try {
                            //执行自定义注解的方法
                            System.out.println("nexttime: " + nextTime);
                            this.method.invoke(this.invokeMethod);
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            e.printStackTrace();
                            log.error(e);
                        }
                    }
                } else {
                    try {
                        //执行自定义注解的方法
                        this.method.invoke(this.invokeMethod);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                        log.error(e);
                    }
                }
            }
        }
    }
    /**
     * 配置定时器
     *
     * @param taskRegistrar ScheduledTaskRegistrar
     */
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        if (taskRegistrar != null) {
            for (KyTask kt : kyTaskList) {
                Method method = kt.getKyMethod();
                //创建触发器
                KyTrigger trigger = new KyTrigger(kt.getKyScheduled());
                //创建任务
                Job job = new Job(method, trigger);
                //自动将心跳任务加入调度器中
                if ("heartbeatCheck".equals(kt.getKyScheduled().name())) {
                    taskRegistrar.addTriggerTask(job, trigger);
                } else {
                    String beanName = method.getDeclaringClass().getName() + "." + method.getName();
                    //创建任务对象
                    KyTaskDetails taskDetails = new KyTaskDetails();
                    taskDetails.setTaskName(!"".equals(kt.getKyScheduled().name()) ? kt.getKyScheduled().name() : method.getName());
                    taskDetails.setBeanName(beanName);
                    taskDetails.setTrigger(trigger);
                    taskDetails.setTriggerId(0L);
                    taskDetails.setJob(job);
                    //创建持久化对象
                    Map<String, Object> objectMap;
                    try {
                        objectMap = jdbcTemplate.queryForMap("select * from ky_task_details where beanName = '" + beanName + "'");
                    } catch (Exception ex) {
                        objectMap = null;
                    }
                    if(objectMap == null) {
                        //插入数据
                        String insertSql = "insert into ky_task_details (taskName, beanName, state, triggerId) values (?,?,?,?)";
                        KeyHolder keyHolder = new GeneratedKeyHolder();
                        jdbcTemplate.update(new PreparedStatementCreator() {
                            public PreparedStatement createPreparedStatement(
                                    Connection connection) throws SQLException {
                                PreparedStatement ps = connection.prepareStatement(insertSql,
                                        new String[] { "id" });
                                ps.setString(1, taskDetails.getTaskName());
                                ps.setString(2, taskDetails.getBeanName());
                                ps.setInt(3, taskDetails.getState());
                                ps.setLong(4, taskDetails.getTriggerId());
                                return ps;
                            }
                        }, keyHolder);
                        taskDetails.setId(keyHolder.getKey().intValue());
                    }
                    taskDetailMap.put(taskDetails.getBeanName(), taskDetails);
                }
            }
        }
    }
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    /**
     * 获取所有自定义注解,并记录注解和方法的信息
     *
     * @param bean     bean
     * @param beanName beanName
     * @return Object
     * @throws BeansException BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(bean.getClass());
        if (methods != null) {
            for (Method method : methods) {
                KyScheduled annotation = AnnotationUtils.findAnnotation(method, KyScheduled.class);
                if (annotation != null) {
                    KyTask at = new KyTask();
                    at.setKyScheduled(annotation);
                    at.setKyMethod(method);
                    kyTaskList.add(at);
                }
            }
        }
        return bean;
    }
    /**
     * 获取分布式锁
     *
     * @param lockName 锁名称
     * @param second   加锁时间(秒)
     * @return 如果获取到锁,则返回lockId值,否则为null
     */
    private String setnxLock(String lockName, int second) {
        synchronized (this) {
            //生成随机的Value值
            String lockId = UUID.randomUUID().toString();
            //抢占锁
            Long lock = this.jedis.setnx(lockName, lockId);
            if (lock == 1) {
               //拿到Lock,设置超时时间
               this.jedis.expire(lockName, second - 1);
               return lockId;
            }
            return null;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Scheduled注解是Spring框架提供的一种方式,用于实现定时任务的调度。它可以配合分布式任务调度框架来实现分布式定时器的功能。 要实现分布式定时器,可以使用以下步骤: 1. 首先,需要引入Spring框架和相关的依赖。可以在项目的pom.xml文件中添加spring-boot-starter和spring-boot-starter-web依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> ``` 2. 创建一个定时任务类,使用@Scheduled注解标记需要定时执行的方法。该方法会根据注解中的cron表达式或固定的时间间隔来触发执行。 ```java @Component public class MyScheduler { @Scheduled(cron = "0 0 12 * * ?") // 每天中午12点触发执行 public void myTask() { // 定时任务逻辑 } } ``` 3. 在Spring Boot的启动类上添加@EnableScheduling注解,启用定时任务的调度功能。 ```java @SpringBootApplication @EnableScheduling public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } ``` 4. 配置分布式任务调度框架,例如Quartz或Elastic-Job,来实现分布式定时任务的调度和执行。具体的配置方式和使用方法可以参考对应框架的文档。 通过以上步骤,就可以使用@Scheduled注解实现分布式定时器的功能了。定时任务会在指定的时间触发执行,无论是单节点还是多节点部署,都能够按照设定的规则进行执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值