**任务调度:**是指系统为了自动完成特点任务,在约定的特定时刻去执行任务的过程。有了任务调度就不需要人力去实现,系统可以在某个时间自动执行任务。
1.**多线程方式实现:**可以开启一个线程,每sleep一段时间,就去检查是否已到预期执行时间。
public static void main(String[] args) {
/**
* 1.多线程实现任务(每隔5秒钟打印一次“多线程实现任务”)
*/
//任务执行间隔时间
final long timeInterval = 5000;
Runnable runnable = new Runnable() {
@Override
public void run() {
while (true){
try {
System.out.println("多线程实现任务");
Thread.sleep(timeInterval);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
2.jdk也提供了Timer,ScheduledExecutor实现任务调度:
Timer方式实现:
public static void main(String[] args) {
/**
* 2.Timer方式实现任务调度(一秒后开始调度,每2秒执行一次"Timer方式实现任务调度")
*/
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("Timer方式实现任务调度");
}
},1000,2000);//一秒后开始调度,每2秒执行一次
}
}
ScheduledExecutor方法实现:
public static void main(String[] args) {
/**
* 3.ScheduledExecutor方式实现任务调度(一秒后开始调度,每2秒执行一次"ScheduledExecutor方式实现任务调度")
*/
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
service.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
System.out.println("ScheduledExecutor方式实现任务调度");
}
},1,
2, TimeUnit.SECONDS);
}
}
java5推出基于线程池设计的ScheduledExecutor,其设计思想是每个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。
Timer和ScheduledExecutor都仅能提供基于开始时间与重复间隔的任务调度,不能胜任解决更加复杂的调度需求。比如:不能实现设置每月第一天凌晨1点执行任务,复杂调度任务的管理,任务之间传递数据等等。
Quartz是一个更难强大的任务调度框架,它可以满足更多复杂的调度需求,Quartz设计的核心类包括Scheduler,Job以及Trigger。其中,Job负责定义需要执行的任务,Trigger负责设置调度策略,Scheduler将二者组装在一起,并触发任务开始执行。Quanrtz支持简单的按时间间隔调度,还支持按日历调度方式,通过设置CronTrigger表达式(秒,分,时,日,月,周,年)进行任务调度。
Quartz实现方式(两种):
继承Job类的MyJob类,设置任务的明细:
package com.mystudy.scheduler.elastiticjob.quickstart;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
/**
* @author AUAS
*/
public class MyJob implements Job {
public void execute(JobExecutionContext jobExecutionContext) {
System.out.println("todo something");
}
}
QuartzController类:
package com.mystudy.scheduler.elastiticjob.quickstart;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import javax.xml.validation.SchemaFactory;
/**
* @author AUAS
* Quartz实现任务调度
*/
public class QuartzController {
public static void main(String[] args) throws SchedulerException {
/**
* 4.Quartz实现任务调度(每两秒执行一次,输出MyJob.class的内容)
*/
//创建一个Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//创建JobDetail
JobBuilder jobBuilder = JobBuilder.newJob(MyJob.class);
jobBuilder.withIdentity("jobName","jobGroupName");
JobDetail jobDetail = jobBuilder.build();
//方法一,4.1创建触发的CronTrigger 支持按日历调度(每两秒执行一次,输出MyJob.class的内容)
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("triggerName","triggerGroupName")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
.build();
//方法二,4.2创建触发的SimpleTrigger简单的间隔调度(每两秒执行一次,输出MyJob.class的内容)
// SimpleTrigger trigger = TriggerBuilder.newTrigger()
// .withIdentity("triggerName","triggerGroupName")
// .startNow()
// .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever())
// .build();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
Cron表达式用于配置creonTrigger的实例。cron表达式实际上是由七个子表达式组成,至少由六个表达式组成。这些表达式之间用空格分分隔。
如下:
**
Cron表达式格式:秒 分 时 日 月 周 年(可选)。
每个子表达式意思 范围 允许特殊字符
1.Seconds(秒) (0~59) ,-/
2.Minutes(分) (0~59) ,-/
3.Hours(小时) (0~23) ,-/
4.Day-of-Month(天) (1~31,要注意有一些月份没有31天) ,-/?LWC
5.Month(月) (0~11,或者JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC) ,-/
6.Day-of-Week(周) (1~7,1=SUM,或者SUN,MON,TUE,WED,THU,FRI,SAT) ,-/?LC#
7.Year(年)可选 (1970~2022)** ,-*/
例子:“0 0 12 ? * WED” 意思是:每个星期三的中午12点执行
个别子表达式可以包含范围,列表。例如上面例子中的WEB可以换成MON-FRI或MON,WEB,FRI 或者MON-WEB,SAT
特殊字符含义:
** * **:代表所有可能的值。在Month中表示每个月,在Day-of-Month中表示每天,在Hours表示每小时
**- *:表示指定范围
,:表示列出枚举值。在Minutes中,"5,20"表示在5分钟和20分钟触发
/:被用于指定增量。在Minutes中,"0/15"表示从0开始,每15分钟执行一次。“3/20"表示从第三分钟开始,每20分钟执行一次。和"3,23,43"表示第3,23,43分钟触发)的含义一样。
?:用在Day-of-Month和Day-of-Week中,指没有具体的值。当两个子表达式其中一个被指定了值后,为了避免冲突,需要另外一个的值设为?。例如:在每月20日触发调度,不管20号是星期几,只能用如下写法:0 0 0 20 * ? (其中最后的周只能用?而不能用。
L:用在Day-of-month 和 Day-of-week中。它是单词last的缩写。它在这两个子表达式中的含义是不同的。在Day-of-month中表示一个月最后一天,一月31号,4月30号。在Day-of-week中表示一个星期的最后一天,也就是7或者SAT。如果L前面有具体的内容,它就有其它含义了。6L表示这个月的倒数第六天。FRIL表示这个月的最后一个星期五。注意在使用L参数是,不要使用列表或者范围。
W:weekday的缩写。只能用在day-of-month字段。用来描述最接近指定天的工作日(周一到周五)例如在day-of-month中用15W表示最接近这个月第15天的工作日,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周二,那么触发器就在这天触发。注意这个用法只会在当前月计算值,不会越过当前月。W字符只在day-of-month指明一天,不能是一个范围或列表。也可以用LW来指定这个月的最后一个工作日,即最后一个星期五。
#:只能用在day-of-week,用来指定这个月的第几个周几。6#3或者FRI#3指这个月第3个周五(6指周五,3指第3个)。如果指定日期不存在,触发器就不会触发。
下载链接:https://archive.apache.org/dist/zookeeper/
解压下载的包,把zookeeper-3.4.10\conf目录下的zoo_sample.cfg文件名修改为zoo.cfg,如下
然后在zookeeper-3.4.10\bin目录下找到zkServer.cmd,启动Zookeeper,启动成功如下:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mystudy.scheduler</groupId>
<artifactId>elastic-job-quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-core</artifactId>
<version>2.1.5</version>
</dependency>
<!-- lombok组件,实体类不用写get,set方法 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
JobMain.java:
package com.mystudy.scheduler.elastiticjob.quickstart.quick;
import com.dangdang.ddframe.job.config.JobCoreConfiguration;
import com.dangdang.ddframe.job.config.simple.SimpleJobConfiguration;
import com.dangdang.ddframe.job.lite.api.JobScheduler;
import com.dangdang.ddframe.job.lite.config.LiteJobConfiguration;
import com.dangdang.ddframe.job.reg.base.CoordinatorRegistryCenter;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperConfiguration;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;
import com.mystudy.scheduler.elastiticjob.quickstart.job.FileBackupJob;
import com.mystudy.scheduler.elastiticjob.quickstart.model.FileCustom;
/**
* @author AUAS
* 启动任务
*/
public class JobMain {
//zookeeper端口
private static final int ZOOKEEPER_PORT = 2181;
//zookeeper链接字符串 localhost:2181
private static final String ZOOKEEPER_CONNECTION_STRING = "localhost:" + ZOOKEEPER_PORT;
//定时任务命命空间(命名空间可以随意定义)
private static final String JOB_NAMESPACE = "elastic-job-example-java";
/**
* 执行启动任务
*/
public static void main(String[] args) {
//制造一些测试数据
generateTestFiles();
//配置注册中心
CoordinatorRegistryCenter registryCenter = setUpRegistryCenter();
//启动任务
startJob(registryCenter);
}
/**
* zk的配置及创建注册中心
*/
private static CoordinatorRegistryCenter setUpRegistryCenter(){
//zk配置
ZookeeperConfiguration zookeeperConfiguration =
new ZookeeperConfiguration(ZOOKEEPER_CONNECTION_STRING,JOB_NAMESPACE);
//减少zk的超时时间
zookeeperConfiguration.setSessionTimeoutMilliseconds(100);
//创建注册中心
CoordinatorRegistryCenter registryCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
registryCenter.init();
return registryCenter;
}
/**
* 任务的配置和启动
*/
private static void startJob(CoordinatorRegistryCenter registryCenter){
//创建JobCoreConfiguration(String jobName 任务名称, String cron 调度表达式, int shardingTotalCount作业分片数量)
JobCoreConfiguration jobCoreConfiguration =
JobCoreConfiguration.newBuilder("files-job","0/3 * * * * ?",3).build();
//创建SimpleJobConfiguration(FileBackupJob需要调度的任务类)
SimpleJobConfiguration simpleJobConfiguration = new SimpleJobConfiguration(jobCoreConfiguration,
FileBackupJob.class.getCanonicalName());
//创建new JobScheduler
new JobScheduler(registryCenter, LiteJobConfiguration.newBuilder(simpleJobConfiguration).overwrite(true).build()).init();
}
/**
* 制造一些测试数据
* 生成测试文件
*
*/
private static void generateTestFiles(){
for(int i = 1 ;i < 11; i++){
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+10),"文件"+(i+10),"text","content"+ (i+10)));
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+20),"文件"+(i+20),"image","content"+ (i+20)));
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+30),"文件"+(i+30),"radio","content"+(i+30)));
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+40),"文件"+(i+40),"vedio","content"+ (i+40)));
}
System.out.println("生成测试文件");
}
}
FileCustom.java:
package com.mystudy.scheduler.elastiticjob.quickstart.model;
import lombok.Data;
/**
* @author AUAS
*
* 文件对象实体类
*/
@Data
public class FileCustom {
/**
* 标识
*/
private String id;
/**
* 文件名
*/
private String name;
/**
* 文件类型,text,image,radio,vedio
*/
private String type;
/**
* 文件内容
*/
private String content;
/**
* 是否已备份
*/
private Boolean backedUp = false;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Boolean getBackedUp() {
return backedUp;
}
public void setBackedUp(Boolean backedUp) {
this.backedUp = backedUp;
}
public FileCustom(String id, String name, String type, String content) {
this.id = id;
this.name = name;
this.type = type;
this.content = content;
}
}
FileBackupJob.java:
package com.mystudy.scheduler.elastiticjob.quickstart.job;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.mystudy.scheduler.elastiticjob.quickstart.model.FileCustom;
import org.apache.commons.lang3.ObjectUtils;
import javax.swing.plaf.synth.SynthOptionPaneUI;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 文件备份任务。实现SimpleJob
* @author AUAS
*/
public class FileBackupJob implements SimpleJob {
//文件列表(模拟)
public static List<FileCustom> theAllfiles = new ArrayList<FileCustom>();
//每次任务执行要备份的文件数量
private final Integer FETCH_SIZE = 1;
/**
* 任务调度执行方法,实现SimpleJob一定有execute方法
* @param shardingContext
*/
@Override
public void execute(ShardingContext shardingContext) {
System.out.println("作业分片:"+shardingContext.getShardingItem());
//获取未备份文件
List<FileCustom> fileCustoms = fetchUnBackupFiles(FETCH_SIZE);
//进行文件备份
backupFiles(fileCustoms);
}
/**
* 获取未备份的文件
* @param count 文件数量
* @return
*/
public List<FileCustom> fetchUnBackupFiles(int count){
//获取的文件列表
List<FileCustom> files = new ArrayList<FileCustom>();
int num = 0;
for (FileCustom file:theAllfiles) {
if (num >= count){
break;
}
if (!file.getBackedUp()){
files.add(file);
num++;
}
}
System.out.printf("时间:%s,获取文件%d个", LocalDateTime.now(),num);
return files;
}
/**
* 文件备份,把实体类是否备份属性backedUp改为true
*/
public void backupFiles(List<FileCustom> files){
for (FileCustom fileCustom : files){
fileCustom.setBackedUp(true);
System.out.printf("时间:%s,备份文件名称:%s,类型:%s", LocalDateTime.now(),fileCustom.getName(),fileCustom.getType());
}
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mystudy.com</groupId>
<artifactId>elastic-job-springboot</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-spring</artifactId>
<version>2.1.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
<build>
<finalName>${project.nameJ</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>utf-8</encoding>
<useDefaoltDelimiters>true</useDefaoltDelimiters>
</configuration>
</plugin>
</plugins>
</build>
</project>
spring启动类SchedulingBootstrap.java:
package com.mystudy.elastic.job.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author AUAS
* spring boot 启动类 @SpringBootApplication注解标注
*/
@SpringBootApplication
public class SchedulingBootstrap {
public static void main(String[] args) {
SpringApplication.run(SchedulingBootstrap.class,args);
}
}
FileService.java:
package com.mystudy.elastic.job.springboot.service;
import com.mystudy.elastic.job.springboot.model.FileCustom;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author AUAS
*/
@Service
public class FileService {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 获取文件类型为未备份的文件
* @param fileType 文件类型
* @param count 获取条数
* @return
*/
public List<FileCustom> fetchUnBackupFiles(String fileType,Integer count){
List<FileCustom> files = jdbcTemplate.query(
"select * from t_file where type = ? and backedUp = 0 limit 0,?",
new Object[]{fileType,count},new BeanPropertyRowMapper(FileCustom.class)
);
return files;
}
/**
* 备份文件
* @param files 要备份的文件
*/
public void backupFiles(List<FileCustom> files){
for (FileCustom file:files) {
String sql = "update t_file set backedUp = 1 where id = ?";
jdbcTemplate.update(sql,new Object[]{file.getId()});
System.out.println(String.format("线程 %d 已备份文件:%s 文件类型:%s",
Thread.currentThread().getId(),file.getName(),file.getType()));
}
}
}
FileCustom.java:
package com.mystudy.elastic.job.springboot.model;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author AUAS
*
* 文件对象实体类
*/
@NoArgsConstructor
@Data
public class FileCustom {
/**
* 标识
*/
private String id;
/**
* 文件名
*/
private String name;
/**
* 文件类型,text,image,radio,vedio
*/
private String type;
/**
* 文件内容
*/
private String content;
/**
* 是否已备份
*/
private Boolean backedUp = false;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Boolean getBackedUp() {
return backedUp;
}
public void setBackedUp(Boolean backedUp) {
this.backedUp = backedUp;
}
public FileCustom(String id, String name, String type, String content) {
this.id = id;
this.name = name;
this.type = type;
this.content = content;
}
}
FileBackupJob.java:
package com.mystudy.elastic.job.springboot.job;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.mystudy.elastic.job.springboot.model.FileCustom;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 文件备份任务。实现SimpleJob
* @author AUAS
*/
@Component
public class FileBackupJob implements SimpleJob {
//文件列表(模拟)
public static List<FileCustom> theAllfiles = new ArrayList<FileCustom>();
//制造数据
static {
for(int i = 1 ;i < 11; i++){
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+10),"文件"+(i+10),"text","content"+ (i+10)));
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+20),"文件"+(i+20),"image","content"+ (i+20)));
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+30),"文件"+(i+30),"radio","content"+(i+30)));
FileBackupJob.theAllfiles.add(new FileCustom(String.valueOf(i+40),"文件"+(i+40),"vedio","content"+ (i+40)));
}
System.out.println("生成测试文件");
}
//每次任务执行要备份的文件数量
private final Integer FETCH_SIZE = 1;
/**
* 任务调度执行方法,实现SimpleJob一定有execute方法
* @param shardingContext
*/
@Override
public void execute(ShardingContext shardingContext) {
System.out.println("作业分片:"+shardingContext.getShardingItem());
//分片参数,(0=text,1=image,2=radio,3=vedio,参数就是text,image,,,)
String jobParameter = shardingContext.getJobParameter();
//获取未备份文件
List<FileCustom> fileCustoms = fetchUnBackupFiles(FETCH_SIZE);
//进行文件备份
backupFiles(fileCustoms);
}
/**
* 获取未备份的文件
* @param count 文件数量
* @return
*/
public List<FileCustom> fetchUnBackupFiles(int count){
//获取的文件列表
List<FileCustom> files = new ArrayList<FileCustom>();
int num = 0;
for (FileCustom file:theAllfiles) {
if (num >= count){
break;
}
if (!file.getBackedUp()){
files.add(file);
num++;
}
}
System.out.printf("时间:%s,获取文件%d个", LocalDateTime.now(),num);
return files;
}
/**
* 文件备份,把实体类是否备份属性backedUp改为true
*/
public void backupFiles(List<FileCustom> files){
for (FileCustom fileCustom : files){
fileCustom.setBackedUp(true);
System.out.printf("时间:%s,备份文件名称:%s,类型:%s",
LocalDateTime.now(),fileCustom.getName(),fileCustom.getType());
}
}
}
FileBackupJobDb.java
package com.mystudy.elastic.job.springboot.job;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.mystudy.elastic.job.springboot.model.FileCustom;
import com.mystudy.elastic.job.springboot.service.FileService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 文件备份任务。实现SimpleJob
* @author AUAS
*/
@Component
public class FileBackupJobDb implements SimpleJob {
@Autowired
private FileService fileService;
//每次任务执行要备份的文件数量
private final Integer FETCH_SIZE = 1;
/**
* 任务调度执行方法,实现SimpleJob一定有execute方法
* @param shardingContext
*/
@Override
public void execute(ShardingContext shardingContext) {
System.out.println("作业分片:"+shardingContext.getShardingItem());
//分片参数,(0=text,1=image,2=radio,3=vedio,参数就是text,image,,,)
String jobParameter = shardingContext.getShardingParameter();
//获取未备份文件
List<FileCustom> fileCustoms = fetchUnBackupFiles(jobParameter,FETCH_SIZE);
//进行文件备份
backupFiles(fileCustoms);
}
/**
* 获取未备份的文件
* @param count 文件数量
* @return
*/
public List<FileCustom> fetchUnBackupFiles(String fileType ,int count){
//获取的文件列表
List<FileCustom> fileCustomList = fileService.fetchUnBackupFiles(fileType,count);
System.out.printf("时间:%s,获取文件%d个", LocalDateTime.now(),count);
return fileCustomList;
}
/**
* 文件备份,把实体类是否备份属性backedUp改为true
*/
public void backupFiles(List<FileCustom> files){
fileService.backupFiles(files);
}
}
ElasticJobRegistryCenterConfig.java
package com.mystudy.elastic.job.springboot.config;
import com.dangdang.ddframe.job.reg.base.CoordinatorRegistryCenter;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperConfiguration;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author AUAS
*/
@Configuration
public class ElasticJobRegistryCenterConfig {
//zookeeper端口
private final int ZOOKEEPER_PORT = 2181;
//zookeeper链接字符串 localhost:2181
private final String ZOOKEEPER_CONNECTION_STRING = "localhost:" + ZOOKEEPER_PORT;
//定时任务命命空间(命名空间可以随意定义)
private final String JOB_NAMESPACE = "elastic-job-example-java";
/**
* zk的配置及创建注册中心
*/
@Bean(initMethod = "init")
public CoordinatorRegistryCenter setUpRegistryCenter(){
//zk配置
ZookeeperConfiguration zookeeperConfiguration =
new ZookeeperConfiguration(ZOOKEEPER_CONNECTION_STRING,JOB_NAMESPACE);
//减少zk的超时时间
zookeeperConfiguration.setSessionTimeoutMilliseconds(100);
//创建注册中心
CoordinatorRegistryCenter registryCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
return registryCenter;
}
}
ElasticJobConfig.java
package com.mystudy.elastic.job.springboot.config;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.dangdang.ddframe.job.config.JobCoreConfiguration;
import com.dangdang.ddframe.job.config.simple.SimpleJobConfiguration;
import com.dangdang.ddframe.job.lite.config.LiteJobConfiguration;
import com.dangdang.ddframe.job.lite.spring.api.SpringJobScheduler;
import com.dangdang.ddframe.job.reg.base.CoordinatorRegistryCenter;
import com.mystudy.elastic.job.springboot.job.FileBackupJob;
import com.mystudy.elastic.job.springboot.job.FileBackupJobDb;
import com.mystudy.elastic.job.springboot.service.FileService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jboss.netty.util.internal.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author AUAS
*/
@Configuration
public class ElasticJobConfig {
// @Autowired
// SimpleJob fileBackupJob;
@Autowired
FileBackupJobDb fileBackupJob;
@Autowired
CoordinatorRegistryCenter registryCenter;
/**
* 配置任务详细信息
* @param jobClass 任务执行类
* @param cron 执行策略
* @param shardingTotalCount 分片数量
* @param shardingItemParameters 分片个性化参数
* @return
*/
private LiteJobConfiguration createJobConfiguration(final Class<? extends SimpleJob> jobClass,
final String cron,
final int shardingTotalCount,
final String shardingItemParameters){
JobCoreConfiguration.Builder jobCoreConfigurationBuilder =
JobCoreConfiguration.newBuilder(jobClass.getName(), cron,shardingTotalCount);
//设置shardingItemParameters
if (!StringUtils.isEmpty(shardingItemParameters)){
jobCoreConfigurationBuilder.shardingItemParameters(shardingItemParameters);
}
JobCoreConfiguration jobCoreConfiguration = jobCoreConfigurationBuilder.build();
//创建SimpleJobConfiguration
SimpleJobConfiguration simpleJobConfiguration =
new SimpleJobConfiguration(jobCoreConfiguration,jobClass.getCanonicalName());
//创建LiteJobConfiguration
LiteJobConfiguration liteJobConfiguration = LiteJobConfiguration.newBuilder(
simpleJobConfiguration).overwrite(true).build();
return liteJobConfiguration;
}
@Bean(initMethod = "init")
public SpringJobScheduler initSimpleElasticJob(){
//创建SpringJobScheduler
SpringJobScheduler springJobScheduler = new SpringJobScheduler(fileBackupJob,registryCenter,
createJobConfiguration(fileBackupJob.getClass(), "0/3 * * * * ?",
4,"0=text,1=image,2=radio,3=vedio"));
return springJobScheduler;
}
}
测试类添加数据GenerateTestData.java:
package com.mystudy.elastic.job.springboot;
import com.mystudy.elastic.job.springboot.model.FileCustom;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class GenerateTestData {
@Autowired
JdbcTemplate jdbcTemplate;
@Test
public void testGenerateTestData() {
//清除数据
clearTestFiles();
//制造数据
generateTestFiles();
}
/**
* 清除模拟数据
*/
public void clearTestFiles() {
jdbcTemplate.update("delete from t_file");
}
/**
* 创建模拟数据
*/
public void generateTestFiles() {
List<FileCustom> files = new ArrayList<>();
for (int i = 1; i < 11; i++) {
files.add(new FileCustom(String.valueOf(i), "文件" + i, "text", "content" + i));
files.add(new FileCustom(String.valueOf((i + 10)), "文件" + (i + 10), "image", "content" +
(i + 10)));
files.add(new FileCustom(String.valueOf((i + 20)), "文件" + (i + 20), "radio", "content" +
(i + 20)));
files.add(new FileCustom(String.valueOf((i + 30)), "文件" + (i + 30), "vedio", "content" +
(i + 30)));
}
//把数据放到数据库里
for (FileCustom file : files) {
jdbcTemplate.update("insert into t_file (id,name,type,content,backedUp) values (?,?,?,?,?)",
new Object[]{file.getId(), file.getName(), file.getType(), file.getContent(), file.getBackedUp()});
}
}
}
application.properties:
#spring boot配置文件
server.port=56081
spring.application.name=task-scheduling-springboot
logging.level.root = info
#数据源定义
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/study111?useUnicode=true
spring.datasource.username=root
spring.datasource.password=root111
**1.事件追踪:**ElasticJob-Lite在配置中提供了JobEventConfiguration,支持数据库方式配置,会在数据库中自动创建JOB EXECUTION LOG和JOB STATUS TRACE LOG两张表以及若索引,来记录作业的相关信息。
配置方式:
1.1在ElasticJobConfig类增加以下代码:
重新运行项目后,数据库会多出来下面两个记录日志的表:
JOB_EXECUTION_LOG记录每次作业的执行历史。分为两个步骤
1.作业开始执行时向数据库插入数据,除failure cause和complete time外的其他字段均不为空
2.作业完成执行时向数据库更新数据,更新is success, complete time和failure cause(如果作业执行失败)。
JOB_STATUS_TRACE_LOG记录作业状态变更痕迹表。可通过每次作业运行的task id查询作业状态变化的生命周期和运行轨迹。
elastic-job中提供了一个elastic-job-lite-console控制台设计理念
1.本控制台和Elastic ob并无直接关系,是通过读取Elastic lob的注册中心数据展现作业状态,或更新注册中心数据修改全局配置。
2.控制台只能控制作业本身是否运行,但不能控制作业进程的启停,因为控制台和作业本身服务器是完全分布式的,控制台并不能控制作业服务器。
elastic-job-lite-console控制台主要功能
1.查看作业以及服务器状态
2.快捷的修改以及删除作业设置
3.启用和禁用作业
4.跨注册中心查看作业
5 查看作业运行轨迹和运行状本
elastic-job-lite-console下载地址 :
链接:https://pan.baidu.com/s/1tzb9MN5LKrD3YkWWGaab9g?pwd=gpir 提取码:gpir
下载后解压文件夹,进入 bin目录并执行bin start.sh
打开浏览器访问 http://localhost:8899/ 即可访问控制台。8899为默认端口号,可通过启动脚本输入-p自定义端口号。默认账号密码都是root。成功打开如下:
使用Elastic-ob-Lite过程中可能会碰到一些问题,导致作业运行不稳定。由于无法在生产环境调试,通过dump命令可以把作业内部相关信息dump出来,方便开发者debug分析。
(1)开启dump监控端口,并运行程序
修改中ElasticobConfig中的createlobConfiguration方法里obRootConfiguration的配置,开启dump监控端口
JobRootConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).monitorPort(9888).build(); //设dump
(2)windows中安装netcat ( 若操作系统中已经有nc命令,此步骤可略过)
(3)执行dump命令
(4)打开命令行工具,进入netcat-win32-1.12.zip的解压目录,执行以下命令
echo dump | nc 127.0.0.1 9888 > job_debug_dump.txt
会在当前目录生成job_debug_dump.txt文件,打开job_debug_dump.txt后看到
/com.itheima.scheduling.job.FileBackupJob/sharding
/com.itheima.scheduling.job.FileBackupJob/sharding/3