一、任务调度概述
1、什么是任务调度?就是定时JOB,
在什么时间执行代码任务
任务调度场景:网站每天主从新用户1000人,
注册用户信息、登录信息、定时发送到我的邮箱。21:00。
同步job,调用失败之后,有补偿机制,日志+定时JOB分布式解决方案
2、java实现定时任务有几种?
Thread:线程来实现
ThreadTask:
线程池,可定时线程
quartz:定时任务调度框架
SpringBoot内置任务调度
二、java实现定时任务实现方案
1、使用线程的方式来实现定时任务
package com.leeue.com;
/**
* 使用多线程方式实现定时Job
*
* @classDesc: 功能描述:()
* @author:<a href="leeue@foxmail.com">李月</a>
* @Version:v1.0
* @createTime:2018年10月17日 下午5:14:38
*/
public class Test001 {
static int count = 0;
public static void main(String[] args) {
// 思考实现定时任务
new Thread(new Runnable() {
@Override
public void run() {
//监听任务调度
while(true) {
try {
// 每隔一秒的时间 实现定时任务 许多定时任务的底层代码就是这样写的,不过封装的很好而已
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("我是第"+(++count));
}
}
}).start();
}
}
2、使用TimerTask方式来实现定时JOB
package com.leeue.com;
import java.util.Timer;
import java.util.TimerTask;
/**
* 使用 time task方式来实现定时任务
*
* @classDesc: 功能描述:()
* @author:<a href="leeue@foxmail.com">李月</a>
* @Version:v1.0
* @createTime:2018年10月17日 下午5:24:34
*/
public class Test002 {
static int count = 0;
public static void main(String[] args) {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
//需要执行的调度任务
System.out.println("我是第"+(++count));
}
};
Timer timer = new Timer();
//天数
long delay = 0;
//秒数
long period = 3000;
timer.schedule(timerTask, delay,period);
}
}
3、使用线程池的方式来实现定时任务
package com.leeue.com;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 使用多线程的方式来实现
*
* @classDesc: 功能描述:()
* @author:<a href="leeue@foxmail.com">李月</a>
* @Version:v1.0
* @createTime:2018年10月17日 下午5:53:17
*/
public class Test003 {
static int count = 0;
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
//需要执行的调度任务
System.out.println("我是第"+(++count));
}
};
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
// 第二个参数为首次执行的延时时间,第三个参数为定时执行的间隔时间
service.scheduleAtFixedRate(runnable, 1, 1, TimeUnit.SECONDS);
}
}
4、使用Quartz来实现定时调度任务
1、引入依赖文件
<dependencies>
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>
2、任务调度类
public class MyJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("quartz MyJob date:" + new Date().getTime());
}
}
3、任务启动类
package com.leeue.demo;
import java.util.Date;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
public class App {
public static void main(String[] args) throws SchedulerException {
// 1.创建Scheduler的工厂
SchedulerFactory sf = new StdSchedulerFactory();
// 2.从工厂中获取调度器实例
Scheduler scheduler = sf.getScheduler();
// 3.创建JobDetail
JobDetail jb = JobBuilder.newJob(MyJob.class).withDescription("this is a ram job") // job的描述
.withIdentity("ramJob", "ramGroup") // job 的name和group
.build();
// 任务运行的时间,SimpleSchedle类型触发器有效
long time = System.currentTimeMillis() + 3 * 1000L; // 3秒后启动任务
Date statTime = new Date(time);
// 4.创建Trigger
// 使用SimpleScheduleBuilder或者CronScheduleBuilder
Trigger t = TriggerBuilder.newTrigger().withDescription("").withIdentity("ramTrigger", "ramTriggerGroup")
// .withSchedule(SimpleScheduleBuilder.simpleSchedule())
.startAt(statTime) // 默认当前时间启动
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?")) // 两秒执行一次
.build();
// 5.注册任务和定时器
scheduler.scheduleJob(jb, t);
// 6.启动 调度器
scheduler.start();
}
}
三、分布式JOB解决幂等性问题
在集群的情况下,实现定时JOB会产生什么原因?
答:会出现重复执行。
分布式JOB怎么解决幂等问题?
答:
1、使用分布式锁来解决(zk、redis),保证一台服务器执行JOB
2、使用配置文件方式,配置文件加个配置 start=true 或者start=false
如果为true这个tomcat就执行job,如果为false这个tomcat就不执行这个job
。所以打包的时候要打两个war,一个tomcat执行job,一个不执行job。
这样集群就没意义了。
3、使用数据库唯一标识 插入的数据id是唯一,谁插入成功了谁就不报错
缺点:效率低,一般不使用
4、传统任务调度缺点:
1、如果报错了,就会不会再执行了。没有补偿机制。
缺点:1、没有补偿机制,
2、不支持集群
3、不支持路由策略
路由策略:相当于nginx的轮询策略的方式一样的,这里表示分发
到某个job上去进行执行
4、没有统计
5、没有 管理job平台
6、重试多遍,就要发报警邮箱。
7、状态监控
四、分布式任务调度平台 XXL-JOB 概述
xxl job
分布式调度平台图:
分布式JOB调度平台原理: