Elastic-Job分布式任务调度

一.什么是任务调度

**任务调度:**是指系统为了自动完成特点任务,在约定的特定时刻去执行任务的过程。有了任务调度就不需要人力去实现,系统可以在某个时间自动执行任务。

二,任务调度的实现方式:

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();
    }

Elastic-Job分布式任务调度_第1张图片
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秒执行一次
    }
}

Elastic-Job分布式任务调度_第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);
    }
}

Elastic-Job分布式任务调度_第3张图片
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();
    }
}

Elastic-Job分布式任务调度_第4张图片

Cron表达式详解:

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-FRIMON,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个)。如果指定日期不存在,触发器就不会触发。

Elastic-Job分布式任务调度_第5张图片

下载ZooKeeper并解压

下载链接:https://archive.apache.org/dist/zookeeper/
解压下载的包,把zookeeper-3.4.10\conf目录下的zoo_sample.cfg文件名修改为zoo.cfg,如下
Elastic-Job分布式任务调度_第6张图片
然后在zookeeper-3.4.10\bin目录下找到zkServer.cmd,启动Zookeeper,启动成功如下:

Elastic-Job分布式任务调度_第7张图片
Elastic-Job分布式任务调度_第8张图片

Elastic-Job入门例子代码:

Elastic-Job分布式任务调度_第9张图片

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());
        }
    }
}

Elastic-Job分布式任务调度_第10张图片

lastic-Job与spring结合练习代码:

Elastic-Job分布式任务调度_第11张图片

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

Elastic-Job运维信息:

**1.事件追踪:**ElasticJob-Lite在配置中提供了JobEventConfiguration,支持数据库方式配置,会在数据库中自动创建JOB EXECUTION LOG和JOB STATUS TRACE LOG两张表以及若索引,来记录作业的相关信息。
配置方式:
1.1在ElasticJobConfig类增加以下代码:
Elastic-Job分布式任务调度_第12张图片
Elastic-Job分布式任务调度_第13张图片
Elastic-Job分布式任务调度_第14张图片
重新运行项目后,数据库会多出来下面两个记录日志的表:
在这里插入图片描述
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-Job分布式任务调度_第15张图片
Elastic-Job分布式任务调度_第16张图片

dump命令

使用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

你可能感兴趣的:(Elastic-job,分布式,java)