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,当前运⾏的是实例数,作业总数等信息。
添加数据库连接之后可以查看任务的执⾏结果
然后在作业历史中就可以看到任务执⾏历史了。