elastic-job版本 2.1.5,由于master分支使用的式curator5.x,支持zookeeper需要3.6.x,对3.5和3.4支持不是好,所以采用了较低版本的elastic-job源码进行解析
elastic-job启动分析:
elastic-job配置的一个核心类JobScheduler(本文只讲述SimpleJob类型作业), 直接看该类的init方法
public void init() {
// 1.将配置更新到注册中心
LiteJobConfiguration liteJobConfigFromRegCenter = schedulerFacade.updateJobConfiguration(liteJobConfig);
// 2.缓存当前作业分片数到本地
JobRegistry.getInstance().setCurrentShardingTotalCount(liteJobConfigFromRegCenter.getJobName(), liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getShardingTotalCount());
// 3.JobScheduleController 实例,controller类负责控制quartz调度引擎
JobScheduleController jobScheduleController = new JobScheduleController(
createScheduler(), createJobDetail(liteJobConfigFromRegCenter.getTypeConfig().getJobClass()), liteJobConfigFromRegCenter.getJobName());
// 4.缓存作业对应的controller对应的
JobRegistry.getInstance().registerJob(liteJobConfigFromRegCenter.getJobName(), jobScheduleController, regCenter);
// 5.作业开始调度前,更改一些配置()
schedulerFacade.registerStartUpInfo(!liteJobConfigFromRegCenter.isDisabled());
// 6.根据cron表达式开始调度作业
jobScheduleController.scheduleJob(liteJobConfigFromRegCenter.getTypeConfig().getCoreConfig().getCron());
}
Elastic-job启动主要看init方法,第一步将本地的作业配置持久化到注册中心具体如下(configService.persist):
public void persist(final LiteJobConfiguration liteJobConfig) {
// 检测作业实现类名是否一致
checkConflictJob(liteJobConfig);
// 判断当前是否存在config节点,不存在则写入,存在则查看是否允许覆盖当前配置
if (!jobNodeStorage.isJobNodeExisted(ConfigurationNode.ROOT) || liteJobConfig.isOverwrite()) {
jobNodeStorage.replaceJobNode(ConfigurationNode.ROOT, LiteJobConfigurationGsonFactory.toJson(liteJobConfig));
}
}
第二步缓存当前分片到本地,便于后期配置节点更改时进行对比,判断是否需要重新分片。JobRegistry实例采用的是单例模式,也就是说当前server下面的所有作业都会注册到同一个JobRegistry,具体如下:
// jobName对应controller,同时controller里面保存了一个quartz实例
private Map<String, JobScheduleController> schedulerMap = new ConcurrentHashMap<>();
// jobName对应注册中心
private Map<String, CoordinatorRegistryCenter> regCenterMap = new ConcurrentHashMap<>();
// jobName对应作业实例,主要作用就是获取当前server ip和 实例id,instanceId采用的是ip + 分割符 + 进程id,这样可以避免
// 保证同一节点可以存在不同的应用服务
private Map<String, JobInstance> jobInstanceMap = new ConcurrentHashMap<>();
// 保存作业运行信息
private Map<String, Boolean> jobRunningMap = new ConcurrentHashMap<>();
// jobName对应的分片数
private Map<String, Integer> currentShardingTotalCountMap = new ConcurrentHashMap<>();
第三步实例话作业控制器,主要用来控制quartz调度引擎,包括控制调度,暂停,恢复,已经停止。具体属性如下:
// quartz的调度器
private final Scheduler scheduler;
// quartz的jobDetail
private final JobDetail jobDetail;
private final String triggerIdentity;
这里有点要注意的是创建detail时有一个细节,这也是elastic使用quartz时的桥梁
private JobDetail createJobDetail(final String jobClass) {
JobDetail result = JobBuilder.newJob(LiteJob.class).withIdentity(liteJobConfig.getJobName()).build();
result.getJobDataMap().put(JOB_FACADE_DATA_MAP_KEY, jobFacade);
Optional<ElasticJob> elasticJobInstance = createElasticJobInstance();
if (elasticJobInstance.isPresent()) {
result.getJobDataMap().put(ELASTIC_JOB_DATA_MAP_KEY, elasticJobInstance.get());
} else if (!jobClass.equals(ScriptJob.class.getCanonicalName())) {
try {
result.getJobDataMap().put(ELASTIC_JOB_DATA_MAP_KEY, Class.forName(jobClass).newInstance());
} catch (final ReflectiveOperationException ex) {
throw new JobConfigurationException("Elastic-Job: Job class '%s' can not initialize.", jobClass);
}
}
return result;
}
这里使用的job类型是LiteJob,这个类很重,它是quartz调度时会转接到你写的任务类上。
public final class LiteJob implements Job {
// 提供set方法,可以从JobDataMap中自动设置到属性上
@Setter
private ElasticJob elasticJob;
@Setter
private JobFacade jobFacade;
@Override
public void execute(final JobExecutionContext context) throws JobExecutionException {
JobExecutorFactory.getJobExecutor(elasticJob, jobFacade).execute();
}
}
在quartz执行作业时,会执行我们真正的作业。
第四步就是简单的缓存作业的控制器,一个作业对应一个quartz的scheduler
第五步则是在调度作业前进行一些设置,比如注册中心的监听,leader选举,server状态更新,instace实例持久化到注册中心(临时节点,一旦断开连接就会消失),设置分片标记,初始化作业监听等
public void registerStartUpInfo(final boolean enabled) {
// 注册中心节点监听
listenerManager.startAllListeners();
// 选举leader(负责处理分片)
leaderService.electLeader();
// 更新server状态
serverService.persistOnline(enabled);
// 作业实例上线
instanceService.persistOnline();
// 作业分片标记设置
shardingService.setReshardingFlag();
// 初始化作业监听器
monitorService.listen();
// 开始调节分布式作业不一致问题
if (!reconcileService.isRunning()) {
reconcileService.startAsync();
}
}
以上具体细节后面节选再说,第六步开始调度作业,也就是正式的开始调度了,前面的都算是一个铺垫,调度时通过控制器直接调用quartz的scheduler的start方法开始调度作业。
public void scheduleJob(final String cron) {
try {
if (!scheduler.checkExists(jobDetail.getKey())) {
scheduler.scheduleJob(jobDetail, createTrigger(cron));
}
scheduler.start();
} catch (final SchedulerException ex) {
throw new JobSystemException(ex);
}
}
以上是elastic-job的启动过程,下一节会说一下节点监听的具体作用。