DolphinScheduler心臟:Quartz的定時任務排程框架深度解析

海豚调度發表於2024-11-18

Quartz是一個開源的Java作業排程框架,它提供了強大的定時任務排程功能。在DolphinScheduler中,Quartz用於實現定時任務的排程和管理。DolphinScheduler透過QuartzExecutorImpl類與Quartz整合,將工作流及其定時管理操作與Quartz排程框架相結合,實現任務的排程執行。

本文將詳細剖析Quartz的原理機制,以及在Dolphinscheduler中使用Quartz的原理。

Quartz ER圖

file

  1. QRTZ_JOB_DETAILSQRTZ_TRIGGERS 是中心表,定義了任務與觸發器之間的關係;
  2. QRTZ_TRIGGERS 表透過外來鍵關聯了多個觸發器型別表,如 QRTZ_SIMPLE_TRIGGERSQRTZ_CRON_TRIGGERS,用於實現不同型別的觸發方式;
  3. QRTZ_FIRED_TRIGGERS 用於記錄每次任務執行的歷史,與任務和觸發器表都有關聯;
  4. QRTZ_CALENDARS 用於定義觸發器的日曆排除規則,QRTZ_PAUSED_TRIGGER_GRPS 用於管理觸發器組的暫停狀態;
  5. QRTZ_SCHEDULER_STATEQRTZ_LOCKS 主要用於叢集環境中的任務排程協調,確保高可用性。

Dolphinscheduler Quartz使用

新建SHELL任務

file

流程定義上線並配置排程

file
file

定時上線

file

流程例項執行結果

file

原理剖析

建立排程

org.apache.dolphinscheduler.api.controller.SchedulerController#createSchedule
--org.apache.dolphinscheduler.api.service.impl.SchedulerServiceImpl#insertSchedule
....
Schedule scheduleObj = new Schedule();
Date now = new Date();
scheduleObj.setTenantCode(tenantCode);
scheduleObj.setProjectName(project.getName());
scheduleObj.setProcessDefinitionCode(processDefineCode);
scheduleObj.setProcessDefinitionName(processDefinition.getName());
ScheduleParam scheduleParam = JSONUtils.parseObject(schedule, ScheduleParam.class);
scheduleObj.setCrontab(scheduleParam.getCrontab());
scheduleObj.setTimezoneId(scheduleParam.getTimezoneId());
scheduleObj.setWarningType(warningType);
scheduleObj.setWarningGroupId(warningGroupId);
scheduleObj.setFailureStrategy(failureStrategy);
scheduleObj.setCreateTime(now);
scheduleObj.setUpdateTime(now);
scheduleObj.setUserId(loginUser.getId());
scheduleObj.setUserName(loginUser.getUserName());
scheduleObj.setReleaseState(ReleaseState.OFFLINE);
scheduleObj.setProcessInstancePriority(processInstancePriority);
scheduleObj.setWorkerGroup(workerGroup);
scheduleObj.setEnvironmentCode(environmentCode);
scheduleMapper.insert(scheduleObj);
....

核心其實就是向 schedule 表中插入了一條資料而已,如下 :
file

排程上線

org.apache.dolphinscheduler.api.controller.SchedulerController#publishScheduleOnline
--org.apache.dolphinscheduler.api.service.impl.SchedulerServiceImpl#onlineScheduler
----org.apache.dolphinscheduler.api.service.impl.SchedulerServiceImpl#doOnlineScheduler
------org.apache.dolphinscheduler.scheduler.quartz.QuartzScheduler#insertOrUpdateScheduleTask

精簡程式碼如下 : 
// TODO 使用schedule id和projectId封裝 JobKey,比如jobName=job_25(schedulerId),jobGroup=jobgroup_1(projectId)
JobKey jobKey = QuartzTaskUtils.getJobKey(schedule.getId(), projectId);
// TODO 使用projectId和schedule封裝jobDataMap,裡面封裝的是projectId、scheduleId和schedule(JSON儲存)
Map<String, Object> jobDataMap = QuartzTaskUtils.buildDataMap(projectId, schedule);
// TODO 獲取cron表示式
String cronExpression = schedule.getCrontab();
// TODO 獲取時區
String timezoneId = schedule.getTimezoneId();

// TODO 定時排程的開啟時間
Date startDate = DateUtils.transformTimezoneDate(schedule.getStartTime(), timezoneId);
// TODO 定時排程的結束時間
Date endDate = DateUtils.transformTimezoneDate(schedule.getEndTime(), timezoneId);
jobDetail jobDetail = newJob(ProcessScheduleTask.class).withIdentity(jobKey).build();
jobDetail.getJobDataMap().putAll(jobDataMap);
// TODO 建立一個Job
scheduler.addJob(jobDetail, false, true);

// TODO 封裝Trigger
TriggerKey triggerKey = new TriggerKey(jobKey.getName(), jobKey.getGroup());
CronTrigger cronTrigger = newTrigger()
                    .withIdentity(triggerKey)
                    .startAt(startDate)
                    .endAt(endDate)
                    .withSchedule(
                            cronSchedule(cronExpression)
                                    .withMisfireHandlingInstructionIgnoreMisfires()
                                    .inTimeZone(DateUtils.getTimezone(timezoneId)))
                    .forJob(jobDetail).build();

// TODO 開始排程
scheduler.scheduleJob(cronTrigger);

對應的表

儲存每個任務的詳細資訊
file

儲存觸發器的基本資訊,是所有觸發器型別的父表
file

儲存 Cron 表示式觸發器(Cron Trigger)的資訊
file

排程執行

org.apache.dolphinscheduler.scheduler.quartz.ProcessScheduleTask,這個類是 qrtz_job_details 中的 JOB_CLASS_NAME 欄位

protected void executeInternal(JobExecutionContext context) {
    JobDataMap dataMap = context.getJobDetail().getJobDataMap();

    int projectId = dataMap.getInt(QuartzTaskUtils.PROJECT_ID);
    int scheduleId = dataMap.getInt(QuartzTaskUtils.SCHEDULE_ID);

    Date scheduledFireTime = context.getScheduledFireTime();

    Date fireTime = context.getFireTime();

    Command command = new Command();
    command.setCommandType(CommandType.SCHEDULER);
    command.setExecutorId(schedule.getUserId());
    command.setFailureStrategy(schedule.getFailureStrategy());
    command.setProcessDefinitionCode(schedule.getProcessDefinitionCode());
    command.setScheduleTime(scheduledFireTime);
    command.setStartTime(fireTime);
    command.setWarningGroupId(schedule.getWarningGroupId());
    String workerGroup = StringUtils.isEmpty(schedule.getWorkerGroup()) ? Constants.DEFAULT_WORKER_GROUP
            : schedule.getWorkerGroup();
    command.setWorkerGroup(workerGroup);
    command.setTenantCode(schedule.getTenantCode());
    command.setEnvironmentCode(schedule.getEnvironmentCode());
    command.setWarningType(schedule.getWarningType());
    command.setProcessInstancePriority(schedule.getProcessInstancePriority());
    command.setProcessDefinitionVersion(processDefinition.getVersion());

    commandService.createCommand(command);
}

說白了,這個就是quartz的一個回撥函式,最終生成Command。

轉載自Journey
原文連結:https://segmentfault.com/a/1190000045471756

本文由 白鯨開源 提供釋出支援!

相關文章