分布式调度Elastic-job(保姆级)下

5.分⽚概念

        作业分⽚是指任务的分布式执⾏,需要将⼀个任务拆分为多个独⽴的任务项,然后由分布式的应⽤实例分别执⾏某⼀个或者⼏个分布项。

        例如:Elastic-Job快速⼊⻔中⽂件备份的案例,现有两台服务器,每台服务器分别跑⼀个应⽤实例。

        为了快速执⾏作业,那么可以讲任务分成4⽚,每个应⽤实例都执⾏两⽚。作业遍历数据逻辑应为:实例1查找text和image类型⽂件执⾏备份,实例2查找radio和vedio类型⽂件执⾏备份。如果由于服务器拓容应⽤实例数量增加为4,则作业遍历数据的逻辑应为: 4个实例分别处理text,image,radio,video类型的⽂件。

        可以看到,通过对任务的合理分⽚化,从⽽达到任务并⾏处理的效果

分⽚项与业务处理解耦

        Elastic-Job并不直接提供数据处理的功能,框架只会将分⽚项分配⾄各个运⾏中的作业服务器,开发者需要⾃⾏处理分⽚项与真实数据的对应关系

最⼤限度利⽤资源

        将分⽚项设置⼤于服务器的数据,最好是⼤于服务器倍数的数量,作业将会合理利⽤分布式资源,动态的分配分⽚项.

        例如: 3台服务器,分成10⽚,则分⽚项结果为服务器A=0,1,2;服务器B=3,4,5;服务器C=6,7,8,9.如果服务器C奔溃,则分⽚项分配结果为服务器A=0,1,2,3,4;服务器B=5,6,7,8,9.在不丢失分⽚项的情况下,最⼤限度利⽤现有的资源提⾼吞

6.案例改造成任务分⽚

在任务配置类中增加分⽚个数以及分⽚参数.  JobConfig类

private  LiteJobConfiguration createJobConfiguration(Class clazz, String cron, int shardingCount, String shardingParam) {
    JobCoreConfiguration.Builder builder = JobCoreConfiguration.newBuilder(clazz.getSimpleName(), cron, shardingCount);
    //判断shardingParam  分片规则是不是为空
    if(!StringUtils.isEmpty(shardingParam)){
        builder.shardingItemParameters(shardingParam);
    }
    // 定义作业核⼼配置  JobCoreConfiguration.newBuilder("任务名称","多长时间执行一次","分片数量")
    JobCoreConfiguration simpleCoreConfig = builder.build();
    // 定义SIMPLE类型配置  SimpleJobConfiguration("配置名称","执行哪一个处理类")  MyElasticJob.class.getCanonicalName() 这个方法是获取全限类名
    SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, clazz.getCanonicalName());
    // 定义Lite作业根配置
    // LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).overwrite(true).build();  加.overwrite(true)是表示可以覆盖之前写的配置,不加这个的话,那你改的定时器执行时间是不会有变化的
    LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).build();
    return simpleJobRootConfig;
}

//测试调度
@Bean(initMethod = "init")
public SpringJobScheduler fileScheduler(FileCustomElasticJob job, CoordinatorRegistryCenter registryCenter){
    LiteJobConfiguration jobConfiguration = createJobConfiguration(job.getClass(),"0 0/1  * * * ?",4,"0=text,1=image,2=radio,3=vedio");
    return new SpringJobScheduler(job,registryCenter,jobConfiguration);
}

import cn.wolfcode.domain.FileCustom;
import cn.wolfcode.mapper.FileCustomMapper;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


import java.util.List;
import java.util.concurrent.TimeUnit;


/**
* <h1></h1>
*
* @version 1.0
* @date 2022-12-8 17:27
*/
@Component
public class FileCustomElasticJob implements SimpleJob {
    @Autowired
    private FileCustomMapper fileCustomMapper;
    @Override
    public void execute(ShardingContext shardingContext) {
        doWork(shardingContext.getShardingParameter());
    }
    private void doWork(String shardingParameter){
        List<FileCustom> fileList = fileCustomMapper.selectByType(shardingParameter);
        System.out.println("需要备份⽂件个数:"+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);
    }
}

import cn.wolfcode.domain.FileCustom;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;


import java.util.List;


/**
* <h1></h1>
*
* @version 1.0
* @date 2022-12-8 17:24
*/
@Mapper
public interface FileCustomMapper {
    @Select("select * from t_file_custom where backedUp = 0")
    List<FileCustom> selectAll();
    @Update("update t_file_custom set backedUp = #{state} where id = #{id}")
    int changeState(@Param("id") Long id, @Param("state")int state);
    @Select("select * from t_file_custom where backedUp = 0 and type =#{type}}")
    List<FileCustom> selectByType(String type);
}

7.Dataflow类型调度任务(当数据量大的时候,用这个)

        Dataflow类型的定时任务需要实现Dataflowjob接⼝,该接⼝提供2个⽅法供覆盖,分别⽤于抓取(fetchData)和处理(processData)数据,我们继续对例⼦进⾏改造。

        Dataflow类型⽤于处理数据流,他和SimpleJob不同,它以数据流的⽅式执⾏,调⽤fetchData抓取数据,知道抓取不到数据才停⽌作业。

7.1 任务类

import cn.wolfcode.domain.FileCustom;
import cn.wolfcode.mapper.FileCustomMapper;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.dataflow.DataflowJob;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


import java.util.List;
import java.util.concurrent.TimeUnit;


/**
* <h1></h1>
*
* @version 1.0
* @date 2022-12-9 14:56
*/
@Component
//FileCustom   取出来的数据是什么类型的
public class FileDataflowJob implements DataflowJob<FileCustom> {


    @Autowired
    private FileCustomMapper fileCustomMapper;
    //抓取数据
    @Override
    public List<FileCustom> fetchData(ShardingContext shardingContext) {
        //fileCustomMapper.selectLimit(2)  模拟每次取两个
        List<FileCustom> fileList = fileCustomMapper.selectLimit(2);
        return fileList;
    }


    //处理数据
    @Override
    public void processData(ShardingContext shardingContext, List<FileCustom> list) {
        for(FileCustom fileCustom:list){
            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);
    }
}

7.2 配置类

import cn.wolfcode.job.FileCustomElasticJob;
import cn.wolfcode.job.FileDataflowJob;
import com.dangdang.ddframe.job.config.JobCoreConfiguration;
import com.dangdang.ddframe.job.config.JobTypeConfiguration;
import com.dangdang.ddframe.job.config.dataflow.DataflowJobConfiguration;
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.dangdang.ddframe.job.reg.zookeeper.ZookeeperConfiguration;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
* <h1></h1>
*
* @version 1.0
* @date 2022-12-8 16:43
*/
@Configuration
public class JobConfig {


    //注册中心配置
    //@Value("${zookeeper.url}") 从配置文件中拿具体的值
    @Bean
    public CoordinatorRegistryCenter registryCenter(@Value("${zookeeper.url}") String url, @Value("${zookeeper.groupName}")String groupName) {
        //ZookeeperConfiguration("配置zk地址","调度任务的组名")
        ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(url, groupName);
        //设置节点超时时间
        zookeeperConfiguration.setSessionTimeoutMilliseconds(100);
        CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
        regCenter.init();
        return regCenter;
    }




    //定时任务类配置  因为项目中有可能需要写很多个定时器,所以这个不能写死,要变成一个公共的方法
    private  LiteJobConfiguration createJobConfiguration(Class clazz, String cron, int shardingCount, String shardingParam, boolean isDateFlowJob) {
        JobCoreConfiguration.Builder builder = JobCoreConfiguration.newBuilder(clazz.getSimpleName(), cron, shardingCount);
        //判断shardingParam  分片规则是不是为空
        if(!StringUtils.isEmpty(shardingParam)){
            builder.shardingItemParameters(shardingParam);
        }
        // 定义作业核⼼配置  JobCoreConfiguration.newBuilder("任务名称","多长时间执行一次","分片数量")
        JobCoreConfiguration simpleCoreConfig = builder.build();




        JobTypeConfiguration jobConfiguration;
        if(isDateFlowJob){
            //new DataflowJobConfiguration( , , 是否流处理,)
            jobConfiguration = new DataflowJobConfiguration(simpleCoreConfig, clazz.getCanonicalName(),true);
        }else {
            jobConfiguration = new SimpleJobConfiguration(simpleCoreConfig, clazz.getCanonicalName());
        }




        // 定义SIMPLE类型配置  SimpleJobConfiguration("配置名称","执行哪一个处理类")  MyElasticJob.class.getCanonicalName() 这个方法是获取全限类名
        // 定义Lite作业根配置
        // LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).overwrite(true).build();  加.overwrite(true)是表示可以覆盖之前写的配置,不加这个的话,那你改的定时器执行时间是不会有变化的
        LiteJobConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(jobConfiguration).build();
        return simpleJobRootConfig;
    }




//    //测试调度
//    @Bean(initMethod = "init")
//    public SpringJobScheduler testScheduler(MyElasticJob job,CoordinatorRegistryCenter registryCenter){
//        LiteJobConfiguration jobConfiguration = createJobConfiguration(job.getClass(),"0/5 * * * * ?",1);
//        return new SpringJobScheduler(job,registryCenter,jobConfiguration);
//    }




    //测试调度
//    @Bean(initMethod = "init")
//    public SpringJobScheduler fileScheduler(FileCustomElasticJob job, CoordinatorRegistryCenter registryCenter){
//        LiteJobConfiguration jobConfiguration = createJobConfiguration(job.getClass(),"0 0/1  * * * ?",4,"0=text,1=image,2=radio,3=vedio");
//        return new SpringJobScheduler(job,registryCenter,jobConfiguration);
//    }






    //测试调度
    @Bean(initMethod = "init")
    public SpringJobScheduler fileDateFlowScheduler(FileDataflowJob job, CoordinatorRegistryCenter registryCenter){
        LiteJobConfiguration jobConfiguration = createJobConfiguration(job.getClass(),"0 0/1  * * * ?",1,null,true);
        return new SpringJobScheduler(job,registryCenter,jobConfiguration);
    }


}

8.运维管理

8.1 事件追踪

        Elastic-Job-Lite在配置中提供了JobEventConfiguration,⽀持数据库⽅式配置,会在数据库中⾃动创建JOB_EXECUTION_LOG和JOB_STATUS_TRACE_LOG两张表以及若⼲索引来近路作业的相关信息。

8.1.1 修改Elastic-Job配置类

在ElasticJobConfig配置类中注⼊DataSource

@Autowired
private DataSource dataSource;
//测试调度
@Bean(initMethod = "init")
public SpringJobScheduler fileDateFlowScheduler(FileDataflowJob job, CoordinatorRegistryCenter registryCenter){
    LiteJobConfiguration jobConfiguration = createJobConfiguration(job.getClass(),"0 0/1  * * * ?",1,null,true);
    //增加任务事件追踪配置  需要监控哪一个  就在哪一个里面加   配置会在任务执行的时间将任务执行的情况存储到数据源中
    JobEventConfiguration jobEventConfiguration = new JobEventRdbConfiguration(dataSource);
    return new SpringJobScheduler(job,registryCenter,jobConfiguration,jobEventConfiguration);
}

8.1.2 ⽇志信息表

 启动后会发现在elastic-job-demo数据库中新增以下两张表

job_execution_log

记录每次作业的执⾏历史,分为两个步骤:

        1.作业开始执⾏时间想数据库插⼊数据.

        2.作业完成执⾏时向数据库更新数据,更新is_success,complete_time和failure_cause(如果任务执⾏失败)

job_status_trace_log

记录作业状态变更痕迹表,可通过每次作业运⾏的task_id查询作业状态变化的⽣命轨迹和运⾏轨迹.

8.2 运维控制台

elastic-job中提供了⼀个elastic-job-lite-console控制台

设计理念

        1.本 控制台和Elastic-Job并⽆直接关系,是通过读取Elastic-Job的注册中⼼数据展示作业状态,或更新注册中⼼数据修改全局配置。

        2.控制台只能控制任务本身是否运⾏,但不能控制作业进程的启停,因为控制台和作业本身服务器是完全分布式的,控制台并不能控制作业服务器。

主要功能:

        1.查看作业以及服务器状态

        2.快捷的修改以及删除作业配置

        3.启⽤和禁⽤作业

        4.跨注册中⼼查看作业

        5.查看作业运⾏轨迹和运⾏状态

不⽀持项

        1.添加作业,因为作业都是在⾸次运⾏时⾃动添加,使⽤控制台添加作业并⽆必要.直接在作业服务器启动包含Elasitc-Job的作业进程即可。

8.2.1 搭建步骤

        1: 解压缩 elastic-job-lite-console-2.1.5.tar

        2:进⼊bin⽬录,并执⾏:

bin\start.bat

3:打开浏览器访问 http://localhost:8899 ⽤户名: root 密码: root,进⼊之后界⾯如下:

        提供两种⽤户:管理员和访客,管理员拥有全部操作权限,访客仅拥有查看权限。默认管理员账号和⾯膜是root/root,访客⽤户名和密码是guest/guest,通过conf\auth.properties可以修改管理员以及访客⽤户名及密码

8.2.2 配置及使⽤

1:配置注册中⼼地址 先启动zookeeper然后再注册中⼼配置界⾯,点添加

点击提交后,然后点连接(zookeeper必须处于启动状态)

连接成功后,在作业纬度下可以显示该命名空间作业名称,分⽚数量及该作业的cron表达式等信息

在服务器纬度可以查看到服务器ip,当前运⾏的是实例数,作业总数等信息。

添加数据库连接之后可以查看任务的执⾏结果

然后在作业历史中就可以看到任务执⾏历史了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼的动力

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值