Elastic-Job是一个分布式调度解决方案(分布式定时任务调度)可以保证分布式一个任务只有一个节点在运行,支持分片,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成,Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务;Elastic-Job-Cloud采用自研Mesos Framework的解决方案,额外提供资源治理、应用分发以及进程隔离等功能
Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供最轻量级的分布式任务的协调服务,外部依赖仅Zookeeper。目前仅支持Zookeeper,用来记录分片信息。
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.12.RELEASE
com.gs
spring-job
0.0.1-SNAPSHOT
spring-job
Demo project for Spring Boot
1.8
com.dangdang
elastic-job-lite-core
2.1.5
com.dangdang
elastic-job-lite-spring
2.1.5
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-web
com.alibaba
druid-spring-boot-starter
1.1.2
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-test
test
mysql
mysql-connector-java
8.0.11
org.springframework.boot
spring-boot-starter-jdbc
com.dangdang
elastic-job-lite-lifecycle
2.1.5
org.springframework.boot
spring-boot-starter-batch
com.alibaba
fastjson
1.2.39
org.springframework.boot
spring-boot-maven-plugin
# zookeeper集群
elaticjob.zookeeper.server-lists=127.0.0.1:2181
elaticjob.zookeeper.namespace=my-project
# 主要是为了存储任务执行的日志
spring.datasource.druid.log.url=jdbc:mysql://localhost:3306/event_log
spring.datasource.druid.log.username=root
spring.datasource.druid.log.password=root
spring.datasource.druid.log.driver-class-name=com.mysql.jdbc.Driver
# 自动创建更新验证数据库结构
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database=mysql
spring.jpa.show-sql=true
package com.gs.springjob.config;
import com.dangdang.ddframe.job.lite.api.JobScheduler;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperConfiguration;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;
import com.gs.springjob.emnu.JobType;
import com.gs.springjob.jobFactory.JobInstanceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
/**
* 配置zookeeper注册中心
*/
@Configuration
@ConditionalOnExpression("'${elastic.zookeeper.server-lists}'.length() >0")
public class ElasticConfig {
/**
* 用于注册和协调作业分布式行为的组件,目前仅支持Zookeeper
* 初始化配置 initMethod,用于在bean初始化时指定执行方法
* @param serverList
* @param namespace
* @return
*/
//下面的实体bean实例化时将会自动调用init方法,@value将会注入properties文件中的属性
@Bean(initMethod = "init")
public ZookeeperRegistryCenter regCenter(@Value("${elaticjob.zookeeper.server-lists}") String serverList
, @Value("${elaticjob.zookeeper.namespace}") String namespace) {
return new ZookeeperRegistryCenter(new ZookeeperConfiguration(serverList, namespace));
}
@Bean(initMethod = "init")
public JobScheduler settlementJobScheduler(JobInstanceFactory jobInstanceFactory) throws IOException {
return jobInstanceFactory.instanceSimpleJob();
}
}
- 任务监听
@Component
public class CustomerJobListener extends AbstractDistributeOnceElasticJobListener {
@Autowired
private ZookeeperRegistryCenter zookeeperRegistryCenter;
/**
* 设置间隔时间
*/
public CustomerJobListener() {
super(0L, 0L);
}
/**
* 任务开始
* @param shardingContexts
*/
@Override
public void doBeforeJobExecutedAtLastStarted(ShardingContexts shardingContexts) {
System.out.println("任务开始");
}
/**
* 任务结束
* @param shardingContexts
*/
@Override
public void doAfterJobExecutedAtLastCompleted(ShardingContexts shardingContexts) {
System.err.println("任务结束");
}
}
- 数据库配置
@Configuration
public class DataSourceConfig {
@Bean("datasource")
@ConfigurationProperties("spring.datasource.druid.log")
public DataSource dataSourceTow(){
return DruidDataSourceBuilder.create().build();
}
}
- 配置任务这里以简单任务为例
@Component
public class MyJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
System.out.println(String.format("------Thread ID: %s, 任务总片数: %s, " +
"当前分片项: %s,当前参数: %s," +
"当前任务名称: %s,当前任务参数: %s,"+
"当前任务的id: %s"
,
//获取当前线程的id
Thread.currentThread().getId(),
//获取任务总片数
shardingContext.getShardingTotalCount(),
//获取当前分片项
shardingContext.getShardingItem(),
//获取当前的参数
shardingContext.getShardingParameter(),
//获取当前的任务名称
shardingContext.getJobName(),
//获取当前任务参数
shardingContext.getJobParameter(),
//获取任务的id
shardingContext.getTaskId()
));
}
}
@Component
public class ElasticJobHandler {
@Autowired
private ZookeeperRegistryCenter zookeeperRegistryCenter;
@Autowired
private DataSourceConfig dataSourceConfig;
@Autowired
private ElasticJobListener elasticJobListener;
/**
* @param jobName
* @param jobClass
* @param shardingTotalCount
* @param cron
* @param id 数据ID
* @return
*/
private static LiteJobConfiguration.Builder simpleJobConfigBuilder(String jobName,
Class extends SimpleJob> jobClass,
int shardingTotalCount,
String cron,
String id,String parameters) {
return LiteJobConfiguration.newBuilder(new SimpleJobConfiguration(
JobCoreConfiguration
.newBuilder(jobName, cron, shardingTotalCount)
.shardingItemParameters(parameters)
.jobParameter(id).
build(),
jobClass.getCanonicalName()));
}
/**
* 添加一个定时任务
*
* @param jobName 任务名
* @param cron 表达式
* @param shardingTotalCount 分片数
* @param parameters 当前参数
*/
public void addJob(String jobName, String cron, Integer shardingTotalCount, String id,String parameters) {
LiteJobConfiguration jobConfig = simpleJobConfigBuilder(jobName, MyJob2.class, shardingTotalCount, cron, id,parameters)
.overwrite(true).build();
new SpringJobScheduler(new MyJob2(), zookeeperRegistryCenter, jobConfig, elasticJobListener).init();
}
}
Simple类型作业
public class MyElasticJob implements SimpleJob {
@Override
public void execute(ShardingContext context) {
switch (context.getShardingItem()) {
case 0:
// do something by sharding item 0
break;
case 1:
// do something by sharding item 1
break;
case 2:
// do something by sharding item 2
break;
// case n: ...
}
}
}
Dataflow类型作业
Dataflow类型用于处理数据流,需实现DataflowJob接口。该接口提供2个方法可供覆盖,分别用于抓取(fetchData)和处理(processData)数据。
public class MyElasticJob implements DataflowJob {
@Override
public List fetchData(ShardingContext context) {
switch (context.getShardingItem()) {
case 0:
List data = // get data from database by sharding item 0
return data;
case 1:
List data = // get data from database by sharding item 1
return data;
case 2:
List data = // get data from database by sharding item 2
return data;
// case n: ...
}
}
@Override
public void processData(ShardingContext shardingContext, List data) {
// process data
// ...
}
}
流式处理可通过DataflowJobConfiguration配置是否流式处理。
流式处理数据只有fetchData方法的返回值为null或集合长度为空时,作业才停止抓取,否则作业将一直运行下去; 非流式处理数据则只会在每次作业执行过程中执行一次fetchData方法和processData方法,随即完成本次作业。
如果采用流式作业处理方式,建议processData处理数据后更新其状态,避免fetchData再次抓取到,从而使得作业永不停止。 流式数据处理参照TbSchedule设计,适用于不间歇的数据处理。
Script类型作业
Script类型作业意为脚本类型作业,支持shell,python,perl等所有类型脚本。只需通过控制台或代码配置scriptCommandLine即可,无需编码。执行脚本路径可包含参数,参数传递完毕后,作业框架会自动追加最后一个参数为作业运行时信息。
Core对应JobCoreConfiguration,用于提供作业核心配置信息,如:作业名称、分片总数、CRON表达式等。
Type对应JobTypeConfiguration,有3个子类分别对应SIMPLE, DATAFLOW和SCRIPT类型作业,提供3种作业需要的不同配置,如:DATAFLOW类型是否流式处理或SCRIPT类型的命令行等。
Root对应JobRootConfiguration,有2个子类分别对应Lite和Cloud部署类型,提供不同部署类型所需的配置,如:Lite类型的是否需要覆盖本地配置或Cloud占用CPU或Memory数量等。
// 定义作业核心配置
JobCoreConfiguration simpleCoreConfig = JobCoreConfiguration.newBuilder("demoSimpleJob", "0/15 * * * * ?", 10).build();
// 定义SIMPLE类型配置
SimpleJobConfiguration simpleJobConfig = new SimpleJobConfiguration(simpleCoreConfig, SimpleDemoJob.class.getCanonicalName());
// 定义Lite作业根配置
JobRootConfiguration simpleJobRootConfig = LiteJobConfiguration.newBuilder(simpleJobConfig).build();
// 定义作业核心配置
JobCoreConfiguration dataflowCoreConfig = JobCoreConfiguration.newBuilder("demoDataflowJob", "0/30 * * * * ?", 10).build();
// 定义DATAFLOW类型配置
DataflowJobConfiguration dataflowJobConfig = new DataflowJobConfiguration(dataflowCoreConfig, DataflowDemoJob.class.getCanonicalName(), true);
// 定义Lite作业根配置
JobRootConfiguration dataflowJobRootConfig = LiteJobConfiguration.newBuilder(dataflowJobConfig).build();
// 定义作业核心配置配置
JobCoreConfiguration scriptCoreConfig = JobCoreConfiguration.newBuilder("demoScriptJob", "0/45 * * * * ?", 10).build();
// 定义SCRIPT类型配置
ScriptJobConfiguration scriptJobConfig = new ScriptJobConfiguration(scriptCoreConfig, "test.sh");
// 定义Lite作业根配置
JobRootConfiguration scriptJobRootConfig = LiteJobConfiguration.newBuilder(scriptCoreConfig).build();
public class JobDemo {
public static void main(String[] args) {
new JobScheduler(createRegistryCenter(), createJobConfiguration()).init();
}
private static CoordinatorRegistryCenter createRegistryCenter() {
CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(new ZookeeperConfiguration("zk_host:2181", "elastic-job-demo"));
regCenter.init();
return regCenter;
}
private static LiteJobConfiguration createJobConfiguration() {
// 创建作业配置
...
}
}
public class MyElasticJobListener implements ElasticJobListener {
@Override
public void beforeJobExecuted(ShardingContexts shardingContexts) {
// do something ...
}
@Override
public void afterJobExecuted(ShardingContexts shardingContexts) {
// do something ...
}
}
public class TestDistributeOnceElasticJobListener extends AbstractDistributeOnceElasticJobListener {
public TestDistributeOnceElasticJobListener(long startTimeoutMills, long completeTimeoutMills) {
super(startTimeoutMills, completeTimeoutMills);
}
@Override
public void doBeforeJobExecutedAtLastStarted(ShardingContexts shardingContexts) {
// do something ...
}
@Override
public void doAfterJobExecutedAtLastCompleted(ShardingContexts shardingContexts) {
// do something ...
}
}
public class JobMain {
public static void main(String[] args) {
new JobScheduler(createRegistryCenter(), createJobConfiguration(), new MyElasticJobListener()).init();
}
private static CoordinatorRegistryCenter createRegistryCenter() {
CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(new ZookeeperConfiguration("zk_host:2181", "elastic-job-demo"));
regCenter.init();
return regCenter;
}
private static LiteJobConfiguration createJobConfiguration() {
// 创建作业配置
...
}
}
1. 配置类API
JobSettingsAPI 作业配置的API
JobSettings getJobSettings(String jobName) 获取作业设置.
•Parameters: jobName — 作业名称
•Returns: 作业设置对象
void updateJobSettings(JobSettings jobSettings) 更新作业设置.
•Parameters: jobSettings — 作业设置对象
void removeJobSettings(String jobName) 删除作业设置.
•Parameters: jobName — 作业名称
2. 操作类API
2.1 JobOperateAPI 操作作业的API
void trigger(Optional jobName, Optional serverIp) 作业立刻执行.作业在不与上次运行中作业冲突的情况下才会启动, 并在启动后自动清理此标记.
•Parameters: ◦jobName — 作业名称
◦serverIp — 作业服务器IP地址
void disable(Optional jobName, Optional serverIp) 作业禁用.会重新分片.
•Parameters: ◦jobName — 作业名称
◦serverIp — 作业服务器IP地址
void enable(Optional jobName, Optional serverIp) 作业启用.
•Parameters: ◦jobName — 作业名称
◦serverIp — 作业服务器IP地址
void shutdown(Optional jobName, Optional serverIp) 作业关闭.
•Parameters: ◦jobName — 作业名称
◦serverIp — 作业服务器IP地址
void remove(Optional jobName, Optional serverIp) 作业删除.
•Parameters: ◦jobName — 作业名称
◦serverIp — 作业服务器IP地址
2.2 ShardingOperateAPI 操作分片的API
void disable(String jobName, String item) 禁用作业分片.
•Parameters: ◦jobName — 作业名称
◦item — 作业分片项
void enable(String jobName, String item) 启用作业分片.
•Parameters: ◦jobName — 作业名称
◦item — 作业分片项
3. 统计类API
3.1 JobStatisticsAPI 作业状态展示的API
int getJobsTotalCount() 获取作业总数.
•Returns: 作业总数
JobBriefInfo getJobBriefInfo(String jobName) 获取作业简明信息.
•Parameters: jobName — 作业名称
•Returns: 作业简明信息
Collection getAllJobsBriefInfo() 获取所有作业简明信息.
•Returns: 作业简明信息集合
Collection getJobsBriefInfo(String ip) 获取该IP下所有作业简明信息.
•Parameters: ip — 服务器IP
•Returns: 作业简明信息集合
3.2 ServerStatisticsAPI 作业服务器状态展示的API
int getServersTotalCount() 获取作业服务器总数.
•Returns: 作业服务器总数
Collection getAllServersBriefInfo() 获取所有作业服务器简明信息.
•Returns: 作业服务器简明信息集合
3.3 ShardingStatisticsAPI 作业分片状态展示的API
Collection getShardingInfo(String jobName) 获取作业分片信息集合.
•Parameters: jobName — 作业名称
•Returns: 作业分片信息集合
参考地址