升级@Scheduled-分布式定时任务

最近我在对项目的定时任务服务升级,但愿改形成分布式,本来是利用@Scheduled注解实现,然而它并不支持分布式,若是改为quartz或者Spring Cloud Task,感受对于本身这个简单的项目也没有必要。所以,我准备手写一个简单的支持分布式定时调度任务的框架。 git

项目地址是github.com/death00/dis…,欢迎你们star、提意见。github

分析

先分析了一下本身的项目,全都是用的cron表达式,所以执行时间点都是固定的,若是升级为分布式的话,确定是但愿在同一个时间点只有一个应用去执行定时调度。数据库

场景就变成了:框架

多个应用在同一个时间都尝试去执行任务,但最终只有一个应用真正执行。分布式

这样的话,立马就会让人联想到使用去解决,由于是多个应用,因此就是分布式锁。那么,场景又变了:spa

多个应用在同一个时间都尝试去获取分布式锁,只有一个应用能抢到这把锁,抢到锁的应用能够执行定时任务,其余应用则直接放弃,等待下一次执行时间。3d

抢锁的时机是每次定时任务执行以前,这又让我联想到了AOP,那么利用注解也就瓜熟蒂落了。code

分布式锁

既然谈到了分布式锁,那么就想一下,这把锁的名称构成是什么。由于定时任务都有本身专门的时间,若是仅仅采用时间的话,那么当有两个任务同时执行时,则就是在抢一把锁,这一样是不合理的。cdn

因此,锁的名称由两部分组成:任务执行时间、任务名称。索引

实现

实现方案其实已经很成熟了,能够利用Redis数据库Zookeeper等,Redis用的命令是setNx数据库通常都是利用的惟一索引Zookeeper这点我也不是很了解(若是有感兴趣的同窗,欢迎在个人项目中添加)。

个人项目中实现了Redis数据库两种方式,能够看类DisScheduleRedisServiceImplDisScheduleMongodbServiceImpl

注解

其次,我自定义了一个注解DisSchedule

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DisSchedule {

    /**
     * 定时调度任务的名称(默认是方法名)
     */
    String name() default "";

    /**
     * 任务的间隔时间
     */
    int duration();

    /**
     * duration的时间单位(默认:分钟)
     */
    DisScheduleUnit unit() default DisScheduleUnit.MINUTES;
}复制代码

  1. name表明这次定时调度任务的名称。
  2. duration表明任务的间隔时间,配合unit
  3. unit是自定义的时间单位,有秒、分钟。

该注解须要配合@Scheduled共同使用,例如:

@DisSchedule(name = "testSchedule", duration = 1, unit = DisScheduleUnit.MINUTES)
    @Scheduled(cron = "0 0/1 * * * ?")复制代码

cron表达式表明1分钟执行一次,且是在整数分钟开始的时候执行,所以@DisSchedule也须要设置为1分钟的时间。

切面

接下来,咱们只须要在Aspect中定义好切入点(有注解@DisSchedule的方法上),针对这些方法,须要使用Around(环绕加强)进行拦截,由于当抢不到锁的时候,就不容许执行。

具体能够参考类DisScheduleAspect

总结

以上就是我实现的简单的分布式定时任务,虽然简单,但应该能够知足你的基础需求,接下来,我会在这个之上,逐步增长功能(好比监测、失败后预警等)。若是你有什么想法,欢迎在下方留言。

有兴趣的话能够访问个人博客或者关注个人公众号、头条号,说不定会有意外的惊喜。

death00.github.io/

相关文章
相关标签/搜索