ElasticJob-初级使用有这一篇就够了

6,994 阅读6分钟

介绍一下:ElasticJob

ElaticJob,是一个可以定时任务的框架,提供了作业分片策略,定时任务追综,作业运行状态监控,作业监听器,自诊断修复等功能的一个分布式的定时任务框架.注意:该篇节是以SPringElaticJob 为基础的一个介绍

前言:

我们如何实现分布式的定时任务
如果让我们来写一个分布式的定时任务,我们应该如何使用,其实也很简单,我们知道Java的内存或者说直接内存,是在当前任务或者进程开辟的一块内存空间,那么其他进程是无法访问这块内存空间的.我们要实现分布式的定时任务,就需要首先解决这个问题. Zookeeper: 这是一个不错的选择

解决一: 我们来了解一下什么是Zookeeper,这是一个节点的存储框架,而且可以进行节点监听,感知节点的上下线,在连接上Zk之后,我们可以对zk进行节点输出,节点监控,子节点数据监控,节点数据监控,子节点数据监控,等操作,那么通过简短的介绍,我们可以了解到zk是可以满足节点的上下线监控和选举的操作的;

那动态监听节点的问题可以通过ZK来解决,接下来我们要分析一下,如果执行定时任务,那么就需要有cron表达式,和ElasticJob的分片操作,那么他是如何来解决的呢

解决二: 如何实现分片,我们在定义一个定时任务配置的时候,我们可以在参数上添加描述Sharding 分片,也就是说会分几片来进行执行,他有三种规则,公平,哈希ip,自定义分片策略,实现JobShardingStrategy接口并实现sharding方法,接口方法参数为作业服务器IP列表和分片策略选项,分片策略选项包括作业名称,分片总数以及分片序列号和个性化参数对照表,可以根据需求定制化自己的分片策略。

如何写配置:

server:
  port: 8088
elasticjob: #这个是我们自己在初始化的时候区的非框架配置 
  zookeeper-url: localhost:2181   #zookeeper-url 的链接端口
  group-name: elastic-job-group  #这个是zooker 在可视化界面创建项目的名字
spring:
  datasource: #数据源配置
    url: jdbc:mysql://localhost:3306/elastic-job-demo?serverTimezone=GMT%2B8
    driverClassName: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    username: root
    password: admin

一: 简单的一个定时任务如何使用

第一步:Maven依赖(Spring的)

<dependency>
    <groupId>com.dangdang</groupId>
    <artifactId>elastic-job-lite-spring</artifactId>
    <version>2.1.5</version>
</dependency>

第二步:定义一个Zk的连接器

这一步的意义,就是连接zk,一个注册中心,可以让Elasticjob感知到的一个注册中心,并且可以自由使用,也是说这是实现分片的核心技术的一个展现

//简单的
@Configuration
public class CoordinatorRegistryConfig {

    @Bean(initMethod = "init")
    private static CoordinatorRegistryCenter createRegisterCenter(@Value("${elasticjob.zookeeper-url}") String zookeeperUrl, @Value("${elasticjob.group-name}") String group){
        //配置zk地址,调度任务的组名
        ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(zookeeperUrl, group);
        zookeeperConfiguration.setSessionTimeoutMilliseconds(100);
        CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
        return regCenter;
    }
}


第三步:定义一个处理程序 简单的

这是一个简单的,实现定时任务的方法,实现SimpleJob这个接口,实现execute 这个方法,那么当该方法被检测感知到的时候,就会按照定时任务配置的来进行执行
ShardingContext参数介绍:

sharding: 如果写3按照默认的方法,那么根据最基本的分片原则,假设你有三个部署,那么每一次的任务会均匀的分配到三台服务器上
shardingParam: 这个参数和sharding对应的如果sharding=3,那么shardingParam 就需要配置三个分片处理的信息比如 0="中国",1="城市",2="人民",那么这三个参数在分配到不同的执行程序上的时候,只会获取其中一个参数.

image.png

jobName: 作业名称 FileCustomElasticJob 其实就是执行任务的类的名称

taskId: 作业任务ID.
FileCustomElasticJob@-@0,1,2,3@-@READY@-@192.168.31.51@-@20348 拼上了ip 和分多少片执行

shardingTotalCount: 分片总数.
4呗 0123 jobparam: 很显然,一个正常的参数,可以在指定的时候进行获取

@Component
public class MySimpleJob implements SimpleJob {
    //任务调度的方法,每隔一段时间,就会执行
    @Override
    public void execute(ShardingContext shardingContext) {
        //定时任务逻辑
        System.out.println("执行的时间:"+new Date());
    }
}

//数据流
@Component
public class FileCustomElasticJob implements SimpleJob {
    @Autowired
    private FileCustomMapper fileCustomMapper;
    @Override
    public void execute(ShardingContext shardingContext) {
        doWork(shardingContext.getShardingParameter());
    }
    private void doWork(String fileType){
        List<FileCustom> fileList = fileCustomMapper.selecByType(fileType);
        System.out.println("类型为:"+fileType+",文件,需要备份个数:"+fileList.size());
        for(FileCustom fileCustom:fileList){
            backUpFile(fileCustom);
        }
    }
    private void backUpFile(FileCustom fileCustom){
        try {
            //模拟备份动作
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行文件备份====>"+fileCustom);
        fileCustomMapper.changeState(fileCustom.getId(),1);
    }
}

第四步:定义这个简单程序该如何执行

解释:
registryCenter:已经连接zookeeper的一个注册中心管理类
createJobConfiguration:创建一个Job的配置中心
class-字节码
cron 多久执行一次
shardingTotalCount 分多少片
dataFlowType 是否是数据流的定时任务
如果需要设置更多的东西也是可以加字段的比如我写的JobParam是123可以根据业务更换

iniDataflowElasticJob:在这个类里面初始化好该任务fileDataFlowJob定时任务的执行策略, 连接到哪个注册中心,如何执行,如何分片,分片的参数是啥,是否是数据流指定,等信息,然后返回放到Spring容器里面来进行管理

/**
 * Created by wolfcode-lanxw
 */
@Configuration
public class ElasticJobConfig {

    @Autowired
    private CoordinatorRegistryCenter registryCenter;
    @Bean(initMethod = "init")
    public SpringJobScheduler iniDataflowElasticJob(FileDataflowJob fileDataflowJob){
        SpringJobScheduler springJobScheduler = new SpringJobScheduler(
                fileDataflowJob,//业务类
                registryCenter,//zookeeper 类
                createJobConfiguration//配置类
                        (FileDataflowJob.class,"0/5 * * * * ?",2,"0=text,1=image,2=radio,3=vedio",true));
        return springJobScheduler;
    }
    
    private static LiteJobConfiguration createJobConfiguration(final Class<? extends ElasticJob> jobClass,
                                                               final String cron,
                                                               final int shardingTotalCount,
                                                               final String shardingItemParameters,
                                                               boolean dataflowType) {
        // 定义作业核心配置
        JobCoreConfiguration.Builder jobCoreConfigurationBuilder = JobCoreConfiguration.newBuilder(jobClass.getSimpleName(), cron, shardingTotalCount);
        if(!StringUtils.isEmpty(shardingItemParameters)){
            jobCoreConfigurationBuilder.jobParameter("123").shardingItemParameters(shardingItemParameters);  //条件
        }
        JobTypeConfiguration jobConfig = null;
        if(dataflowType){
            jobConfig = new DataflowJobConfiguration(jobCoreConfigurationBuilder.build(),jobClass.getCanonicalName(),true);
        }else{
            // 定义SIMPLE类型配置
            jobConfig = new SimpleJobConfiguration(jobCoreConfigurationBuilder.build(), jobClass.getCanonicalName());
        }
        // 定义Lite作业根配置
        LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(jobConfig).overwrite(true).build();  //这个是 覆盖之前的配置,现在的配置可以生效
        return simpleJobRootConfig;
    }
}

二:数据流定时任务和简单的定时任务有啥区别

这是数据流,需要继承的接口,那么区别是什么,区别就是 这里的定时任务,执行分片的时候是在fetchData来执行的,这个方法,是可以返回一个List的那么 processData 就可以处理定时定时任务返回的数据,数据流的定时任务
我能想到的应用场景: 定时任务分片执行,0=redis 进行缓存数据处理,1=mysql 定时任务落盘 2=报表统计,三个任务在三个不同的服务下来执行.


//数据流分布式作业接口.
public interface DataflowJob<T> extends ElasticJob {
    
    /**
     * 获取待处理数据.
     * @param shardingContext 分片上下文
     * @return 待处理的数据集合
     */
    List<T> fetchData(ShardingContext shardingContext);
    /**
     * 处理数据.
     * @param shardingContext 分片上下文
     * @param data 待处理数据集合
     */
    void processData(ShardingContext shardingContext, List<T> data);
}

强化理解 这个分片就意味着,五秒钟内执行一次,分词通过分片的服务器来分发不用的分片参数,不同的服务器,来进行处理,不同的分片参数 下面这张截图一共有四个,第一个只会接受image 那么第二个只会接受radio 这样子依次退推下去,这样做的好处就是,不用担心分布式部署,重复执行的问题

image.png

@Component
public class FileCustomElasticJob implements SimpleJob {
    @Autowired
    private FileCustomMapper fileCustomMapper;
    @Override
    public void execute(ShardingContext shardingContext) {
        doWork(shardingContext.getShardingParameter());
    }
    private void doWork(String fileType){
        List<FileCustom> fileList = fileCustomMapper.selecByType(fileType);
        System.out.println("类型为:"+fileType+",文件,需要备份个数:"+fileList.size());
        for(FileCustom fileCustom:fileList){
            backUpFile(fileCustom);
        }
    }
    private void backUpFile(FileCustom fileCustom){
        try {
            //模拟备份动作
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("执行文件备份====>"+fileCustom);
        fileCustomMapper.changeState(fileCustom.getId(),1);
    }
}