如果你需要管理job,可以考虑使用elasticjob。
如果你想动态添加job ,也可以考虑使用elasticjob。
如果对系统可靠性,稳定性和服务器弹性等要求比较高,还可以考虑elasticjob。
Elastic-Job是当当网基于Zookepper,Quartz开发并且开源的Java分布式定时任务,解决Quartz不支持分布式的弊端。它由两个相
互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。
Elastic-Job采用去中心化设计,主要分为注册中心、数据分片、分布式协调、定时任务处理和定制化流程型任务等模块。
中心化 | 去中心化 | |
实现难度 | 难 | 易 |
部署难度 | 难 | 易 |
触发时间统一控制 | 可以 | 不可以 |
触发延迟 | 有 | 无 |
异构语言支持 | 容易 | 困难 |
Elastic-Job提供Simple、Dataflow和Script 3种作业类型。方法参数shardingContext包含作业配置、片和运行时信息。可通过getShardingTotalCount(), getShardingItem()等方法分别获取分片总数,运行在本作业服务器的分片序列号等。
io.elasticjob
elastic-job-lite-core
${latest.release.version}
io.elasticjob
elastic-job-lite-spring
${latest.release.version}
Elasti-Job配置分成3个层级,Core, Type和Root。
补充:启动zookepper,通过spring启动配置,作业就能加载。
全部配置参考配置手册
JobCoreConfiguration属性
创建一个简单的springboot项目,maven依赖和上边差不多,使用yaml进行相关属性的配置,主要配置的是数据库连接池,jpa
elasticjob:
serverlists: 172.31.31.48:2181
namespace: boot-job
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&verifyServerCertificate=false&useSSL=false&requireSSL=false
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
type: com.zaxxer.hikari.HikariDataSource
# 自动创建更新验证数据库结构
jpa:
hibernate:
ddl-auto: update
show-sql: true
database: mysql
使用java配置实现,代替官方文档的xml配置
@Configuration
@Data
@ConfigurationProperties(prefix = "elasticjob")
public class ElasticJobConfig {
private String serverlists;
private String namespace;
@Resource
private HikariDataSource dataSource;
@Bean
public ZookeeperConfiguration zkConfig() {
return new ZookeeperConfiguration(serverlists, namespace);
}
@Bean(initMethod = "init")
public ZookeeperRegistryCenter regCenter(ZookeeperConfiguration config) {
return new ZookeeperRegistryCenter(config);
}
/**
* 将作业运行的痕迹进行持久化到DB
*
* @return
*/
@Bean
public JobEventConfiguration jobEventConfiguration() {
return new JobEventRdbConfiguration(dataSource);
}
@Bean
public ElasticJobListener elasticJobListener() {
return new ElasticJobListener(100, 100);
}
}
所有相关的配置到这里就已经OK了,接下来开始具体的编码实现。
定时任务实现
先实现一个自己的任务类,需要实现elastic-job提供的SimpleJob接口,实现它的execute(ShardingContext shardingContext)方法
@Slf4j
public class MyElasticJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
//打印出任务相关信息,JobParameter用于传递任务的ID
log.info("任务名:{}, 片数:{}, id={}", shardingContext.getJobName(), shardingContext.getShardingTotalCount(),
shardingContext.getJobParameter());
}
}
接下来实现一个分布式的任务监听器,如果任务有分片,分布式监听器会在总的任务开始前执行一次,结束时执行一次。监听器在之前的ElasticJobConfig已经注册到了Spring容器之中。
public class ElasticJobListener extends AbstractDistributeOnceElasticJobListener {
@Resource
private TaskRepository taskRepository;
public ElasticJobListener(long startedTimeoutMilliseconds, long completedTimeoutMilliseconds) {
super(startedTimeoutMilliseconds, completedTimeoutMilliseconds);
}
@Override
public void doBeforeJobExecutedAtLastStarted(ShardingContexts shardingContexts) {
}
@Override
public void doAfterJobExecutedAtLastCompleted(ShardingContexts shardingContexts) {
//任务执行完成后更新状态为已执行
JobTask jobTask = taskRepository.findOne(Long.valueOf(shardingContexts.getJobParameter()));
jobTask.setStatus(1);
taskRepository.save(jobTask);
}
}
实现一个ElasticJobHandler,用于向Elastic-job中添加指定的作业配置,作业配置分为3级,分别是JobCoreConfiguration,JobTypeConfiguration和LiteJobConfiguration。LiteJobConfiguration使用JobTypeConfiguration,JobTypeConfiguration使用JobCoreConfiguration,层层嵌套。
@Component
public class ElasticJobHandler {
@Resource
private ZookeeperRegistryCenter registryCenter;
@Resource
private JobEventConfiguration jobEventConfiguration;
@Resource
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) {
return LiteJobConfiguration.newBuilder(new SimpleJobConfiguration(
JobCoreConfiguration.newBuilder(jobName, cron, shardingTotalCount).jobParameter(id).build(), jobClass.getCanonicalName()));
}
/**
* 添加一个定时任务
*
* @param jobName 任务名
* @param cron 表达式
* @param shardingTotalCount 分片数
*/
public void addJob(String jobName, String cron, Integer shardingTotalCount, String id) {
LiteJobConfiguration jobConfig = simpleJobConfigBuilder(jobName, MyElasticJob.class, shardingTotalCount, cron, id)
.overwrite(true).build();
new SpringJobScheduler(new MyElasticJob(), registryCenter, jobConfig, jobEventConfiguration, elasticJobListener).init();
}
}
到这里,elastic-job的注册中心,数据源相关配置,以及动态添加的逻辑已经做完了,接下来在service中调用上面写好的方法,验证功能是否正常。
编写一个ElasticJobService类,扫描数据库中状态为0的任务,并且把这些任务添加到Elastic-job中,这里的相关数据库操作使用了spring-data-jpa,dao层相关代码就不贴了,可以在源码中查看。
@Service
public class ElasticJobService {
@Resource
private ElasticJobHandler jobHandler;
@Resource
private TaskRepository taskRepository;
/**
* 扫描db,并添加任务
*/
public void scanAddJob() {
Specification query = (Specification
.and(criteriaBuilder.equal(root.get("status"), 0));
List
jobTasks.forEach(jobTask -> {
Long current = System.currentTimeMillis();
String jobName = "job" + jobTask.getSendTime();
String cron;
//说明消费未发送,但是已经过了消息的发送时间,调整时间继续执行任务
if (jobTask.getSendTime() < current) {
//设置为一分钟之后执行,把Date转换为cron表达式
cron = CronUtils.getCron(new Date(current + 60000));
} else {
cron = CronUtils.getCron(new Date(jobTask.getSendTime()));
}
jobHandler.addJob(jobName, cron, 1, String.valueOf(jobTask.getId()));
});
}
}
在Junit中添加几条测试数据
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class JobTaskTest {
@Resource
private TaskRepository taskRepository;
@Test
public void add() {
//生成几个任务,第一任务在三分钟之后
Long unixTime = System.currentTimeMillis() + 60000;
JobTask task = new JobTask("test-msg-1", 0, unixTime);
taskRepository.save(task);
unixTime += 60000;
task = new JobTask("test-msg-2", 0, unixTime);
taskRepository.save(task);
unixTime += 60000;
task = new JobTask("test-msg-3", 0, unixTime);
taskRepository.save(task);
unixTime += 60000;
task = new JobTask("test-msg-4", 0, unixTime);
taskRepository.save(task);
}
}
此时,数据库中多了四条状态为0的数据
最后,就可以开始验证整个流程了,代码如下
@SpringBootApplication
public class ElasticJobApplication implements CommandLineRunner {
@Resource
private ElasticJobService elasticJobService;
public static void main(String[] args) {
SpringApplication.run(ElasticJobApplication.class, args);
}
@Override
public void run(String... strings) throws Exception {
elasticJobService.scanAddJob();
}
}
可以看到,在启动过程中,多个任务被加入到了Elastic-job中,并且一小段时间之后,任务一次执行,执行成功之后,因为我们配置了监听器,会打印数据库的更新SQL,当任务执行完成,再查看数据库,发现状态也更改成功。数据库中同时也会多出两张表JOB_EXECUTION_LOG,JOB_STATUS_TRACE_LOG,这是我们之前配置的JobEventConfiguration,通过数据源持久化了作业配置的相关数据,这两张表的数据可以供Elastic-job提供的运维平台使用,具体请查看官方文档。
以上 主要实现了动态添加job 功能 还可以动态管理job,例如动态删除,修改等。
1.下载或者克隆elastic-job源码
地址:https://github.com/dangdangdotcom/elastic-job
2.maven编译安装
进入到elastic-job目录,按住Shift+鼠标右键,选择“在此处打开命令窗口(W)”,执行如下命令:
等待编译安装结束
3.解压上一步打好的包
路径:elastic-job\elastic-job-lite\elastic-job-lite-console\target\elastic-job-lite-console-2.1.5.tar.gz
elastic-job-lite-console-2.1.5\bin目录下是启动脚本
windows环境用:start.bat
linux环境用:start.sh
elastic-job-lite-console-2.1.5\conf目录下是配置文件auth.properties,配置的用户名和密码
root.username=root
root.password=root
guest.username=guest
guest.password=guest
4.以windows环境为例,双击start.bat启动
打开启动脚本看到启动的端口:8899
@echo off
if ""%1"" == ""-p"" goto doSetPort
if ""%1"" == """" goto doStart
echo Usage: %0 [OPTIONS]
echo -p [port] Server port (default: 8899)
goto end
:doSetPort
shift
set PORT=%1
:doStart
set CFG_DIR=%~dp0%..
set CLASSPATH=%CFG_DIR%
set CLASSPATH=%~dp0..\lib\*;%CLASSPATH%
set CONSOLE_MAIN=io.elasticjob.lite.console.ConsoleBootstrap
echo on
if ""%PORT%"" == """" set PORT=8899
java -cp "%CLASSPATH%" %CONSOLE_MAIN% %PORT%
:end
https://www.cnblogs.com/yushangzuiyue/p/9655847.html
https://www.lmlphp.com/user/1387/article/item/29572
https://blog.csdn.net/oppo5630/article/details/79963490
https://www.cnblogs.com/liugx/p/9855612.html
https://blog.csdn.net/zhpengfei0915/article/details/80262817