任务调度
1、作用
- 单机定时执行任务;
- 分布式定时任务:
把分散的,可靠性差的定时执行任务纳入统一的平台,并实现集群管理调度
和分布式部署的一种定时任务的管理方式。
2、任务调度场景
- 每天注册新用户、登陆信息:定时发送到邮箱
- 同步任务:多次尝试调用失败,补偿机制(日志+定时job)
- 其他
3、java实现定时方式
- Thread.sleep
- TimeTask
- 线程池:可定时线程
- quartz: 定时调度框架
- springboot内置定时任务调度
基本三种方式
- 多线程
public class MulThreadTask {
private static int count = 0;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("print sequence:" + (count++));
}
}).start();
}
}
- TimerTask
public class TimeTask01 {
static int count =0;
@SuppressWarnings("AlibabaAvoidUseTimer")
public static void main(String[] args) {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("sequence:" + (count++));
}
};
Timer timer = new Timer();
long initialDelay = 0;
long period = 1000;
timer.scheduleAtFixedRate(timerTask, initialDelay, period);
}
}
- 线程池
public class ThreadPoolTask {
private static int count = 0;
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("sequence:" + (count++));
}
}, 1, 1,TimeUnit.SECONDS);
}
}
Quartz
public class MyJob implements Job {
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("quartz MyJob date:" + new Date().getTime());
}
}
//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();
}
分布式调度框架
1、elastic-job
elastic-job 是由当当网基于quartz 二次开发之后的分布式调度解决方案 , 由两个相对独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成 。
.
Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务。
Elastic-Job-Cloud使用Mesos + Docker(TBD)的解决方案,额外提供资源治理、应用分发以及进程隔离等服务
特点
- 基于quartz 定时任务框架为基础的,因此具备quartz的大部分功能
- 使用zookeeper做协调,调度中心,更加轻量级
- 支持任务的分片
- 支持弹性扩容 , 可以水平扩展 , 当任务再次运行时,会检查当前的服务器数量,重新分片,分片结束之后才会继续执行任务
- 失效转移,容错处理,当一台调度服务器宕机或者跟zookeeper断开连接之后,会立即停* 止作业,然后再去寻找其他空闲的调度服务器,来运行剩余的任务
提供运维界面,可以管理作业和注册中心。
2、xxl-job
由个人开源的一个轻量级分布式任务调度框架 ,主要分为 调度中心和执行器两部分 , 调度中心在启动初始化的时候,会默认生成执行器的RPC代理
对象(http协议调用), 执行器项目启动之后, 调度中心在触发定时器之后通过jobHandle 来调用执行器项目里面的代码,核心功能和elastic-job差不多
,同时技术文档比较完善
3、quartz
quartz 的常见集群方案如下,通过在数据库中配置定时器信息, 以数据库悲观锁的方式达到同一个任务始终只有一个节点在运行,
XXLJOB
步骤:
① 部署: xxl-job-admin 作为注册中心
② 创建执行器(具体调度地址) 可以支持集群
③ 配置文件需要填写xxl-job注册中心地址
④ 每个具体执行job服务器需要创建一个netty连接端口号
⑤ 需要执行job的任务类,集成IJobHandler抽象类注册到job容器中
⑥ Execute方法中编写具体job任务
# web port
server.port=8084
# log config
logging.config=classpath:logback.xml
# xxl-job
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://127.0.0.1:8081/xxl-job-admin
### xxl-job executor address
xxl.job.executor.appname=test
xxl.job.executor.ip=192.168.1.3
xxl.job.executor.port=9999
### xxl-job log path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler/
### xxl-job, access token
xxl.job.accessToken=
@Configuration
@ComponentScan(basePackages = "com.xxl.job.executor.service.jobhandler")
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
@Value("${xxl.job.admin.addresses}")
private String addresses;
@Value("${xxl.job.executor.appname}")
private String appname;
@Value("${xxl.job.executor.ip}")
private String ip;
@Value("${xxl.job.executor.port}")
private int port;
@Value("${xxl.job.executor.logpath}")
private String logpath;
@Value("${xxl.job.accessToken}")
private String accessToken;
@Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobExecutor xxlJobExecutor = new XxlJobExecutor();
xxlJobExecutor.setIp(ip);
xxlJobExecutor.setPort(port);
xxlJobExecutor.setAppName(appname);
xxlJobExecutor.setAdminAddresses(addresses);
xxlJobExecutor.setLogPath(logpath);
xxlJobExecutor.setAccessToken(accessToken);
return xxlJobExecutor;
}
}
@JobHander(value = "demoJobHandler")
@Service
public class DemoJobHandler extends IJobHandler {
@Value("${xxl.job.executor.port}")
private String port;
@Override
public ReturnT<String> execute(String... params) throws Exception {
XxlJobLogger.log("XXL-JOB, Hello World." + port);
System.out.println("XXL-JOB, Hello World." + port);
for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
// TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
}
}