Java定时任务与分布式定时任务
文章目录
业务场景:
- 订单下单之后
15分钟
后,用户未付款,系统需要自动取消订单
。 - 红包
24小时
未被查收,需要延迟执退还
业务; - 超过7天,
自动收货
1. JDK原生
使用JUC
提供的newScheduledThreadPool
来执行定时任务
public class ScheduledExecutor {
public static void main(String[] args) {
// 创建任务队列,起10个线程
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
// 执行任务:1秒 后开始执行,每5秒执行一次
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务: "+new Date());
}
},1,5, TimeUnit.SECONDS);
}
}
2. Spring
使用Spring提供的@Scheduled
配置任务类,在启动类上添加@EnableScheduling
@Component
public class ScheduleWork {
@Scheduled(cron = "5/3 * * * * ? ")
public void doJob() {
System.out.println("spring 定时器 :"+ new Date());
}
}
// 启动类加上注解,表示启用定时任务
@EnableScheduling
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
或者使用@Configuration
定义配置类注解,在任务类上同时使用@EnableScheduling
和@Scheduled
@Configuration
@EnableScheduling
public class SomeJob {
@Scheduled(cron = "5/3 * * * * ? ")
public void someTask() {
System.out.println("更改注解位置的方式 ");
}
}
这种方式有个缺点,那就是执行周期写死在代码里了,没有办法动态改变,要想改变只能修改代码在重新部署启动微服务.
3.Spring + 数据库
-
启动类
@EnableScheduling @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
-
配置类
@Configuration public class ScheduleConfig implements SchedulingConfigurer { @Autowired private ApplicationContext context; @Autowired private SpringScheduleService scheduleService; @Override public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { // 查询出所有的任务列表,这里使用的是mybatis,查询的MySQL数据库、 List<SpringScheduledCron> workList = scheduleService.findAllWorkList(); for (SpringScheduledCron scheduledCron : workList) { Class<?> clazz; Object task; try { // 使用反射获取Class类对象 clazz = Class.forName(scheduledCron.getCronKey()); // 获取spring容器中初始化的bean对象 task = context.getBean(clazz); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("该class有误:"+scheduledCron.getCronKey() + "----",e); } // 所有定时任务类在微服务启动的时候,就会被自动注册到Spring的定时任务里 // 动态改变执行周期 scheduledTaskRegistrar.addTriggerTask((Runnable) task,triggerContext -> { // 获取定时任务表达式,执行时间 String cronExpression = scheduleService.findByCronKey(scheduledCron.getCronKey()).getCronExpression(); return new CronTrigger(cronExpression).nextExecutionTime(triggerContext); }); } } // 开启线程池,去处理多个定时任务 @Bean public Executor taskExecutor() { return Executors.newScheduledThreadPool(10); } }
-
任务类
@Component public class DynamicTask implements Runnable { private Logger logger = LoggerFactory.getLogger(getClass()); private int i; @Override public void run() { logger.info("Task---线程 id :{}, 任务执行 :{}",Thread.currentThread().getId(),++i); } }
4.Spring+Redis
同上,只是将原本放在关系型数据中的任务列表,存储在Redis
中
5.分布式定时任务
RabbitMQ
最适合此类场景的,是使用消息队列的延迟队列
。可以查看这篇文章:《RabbitMQ消息中间件技术精讲(五)》
quartz
依赖于MySQL,使用相对简单,可多节点部署,通过竞争数据库锁来保证只有一个节点执行任务。没有图形化管理页面,使用相对麻烦。
elastic-job-lite
依赖于Zookeeper,通过zookeeper的注册与发现,可以动态的添加服务器。
xxl-job
国产,依赖于MySQL,基于竞争数据库锁保证只有一个节点执行任务,支持水平扩容。可以手动增加定时任务,启动和暂停任务。