/** * Copyright 2016 vip.com. * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. * </p> */ package com.vip.saturn.job.basic; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import org.apache.curator.framework.CuratorFramework; import org.quartz.SchedulerException; import org.quartz.Trigger; import org.quartz.spi.OperableTrigger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.vip.saturn.job.exception.JobException; import com.vip.saturn.job.executor.LimitMaxJobsService; import com.vip.saturn.job.executor.SaturnExecutorService; import com.vip.saturn.job.internal.analyse.AnalyseService; import com.vip.saturn.job.internal.config.ConfigurationService; import com.vip.saturn.job.internal.config.JobConfiguration; import com.vip.saturn.job.internal.control.ReportService; import com.vip.saturn.job.internal.election.LeaderElectionService; import com.vip.saturn.job.internal.execution.ExecutionContextService; import com.vip.saturn.job.internal.execution.ExecutionService; import com.vip.saturn.job.internal.failover.FailoverService; import com.vip.saturn.job.internal.listener.ListenerManager; import com.vip.saturn.job.internal.offset.OffsetService; import com.vip.saturn.job.internal.server.ServerService; import com.vip.saturn.job.internal.sharding.ShardingService; import com.vip.saturn.job.internal.statistics.StatisticsService; import com.vip.saturn.job.internal.storage.JobNodeStorage; import com.vip.saturn.job.reg.base.CoordinatorRegistryCenter; import com.vip.saturn.job.reg.zookeeper.ZkCacheManager; import com.vip.saturn.job.threads.ExtendableThreadPoolExecutor; import com.vip.saturn.job.threads.SaturnThreadFactory; import com.vip.saturn.job.threads.TaskQueue; import com.vip.saturn.job.trigger.SaturnScheduler; /** * 作业调度器. * @author dylan.xue */ public class JobScheduler { static Logger log = LoggerFactory.getLogger(JobScheduler.class); private String jobName; private String executorName; /** since all the conf-node values will be gotten from zk-cache. use this to compare with the new values. */ private JobConfiguration previousConf = new JobConfiguration(null, null); private final JobConfiguration currentConf; private final CoordinatorRegistryCenter coordinatorRegistryCenter; private final ListenerManager listenerManager; private final ConfigurationService configService; private final LeaderElectionService leaderElectionService; private final ServerService serverService; private final ReportService reportService; private final ShardingService shardingService; private final ExecutionContextService executionContextService; private final ExecutionService executionService; private final FailoverService failoverService; private final StatisticsService statisticsService; private final OffsetService offsetService; private final AnalyseService analyseService; private final LimitMaxJobsService limitMaxJobsService; private final JobNodeStorage jobNodeStorage; private final ZkCacheManager zkCacheManager; private ExecutorService executorService; private AbstractElasticJob job; private SaturnExecutorService saturnExecutorService; public JobScheduler(final CoordinatorRegistryCenter coordinatorRegistryCenter, final JobConfiguration jobConfiguration) { this.jobName = jobConfiguration.getJobName(); this.executorName = coordinatorRegistryCenter.getExecutorName(); this.currentConf = jobConfiguration; this.coordinatorRegistryCenter = coordinatorRegistryCenter; this.jobNodeStorage = new JobNodeStorage(coordinatorRegistryCenter, jobConfiguration); initExecutorService(); JobRegistry.addJobScheduler(executorName, jobName, this); zkCacheManager = new ZkCacheManager((CuratorFramework) coordinatorRegistryCenter.getRawClient(), jobName, executorName); configService = new ConfigurationService(this); leaderElectionService = new LeaderElectionService(this); serverService = new ServerService(this); shardingService = new ShardingService(this); executionContextService = new ExecutionContextService(this); executionService = new ExecutionService(this); failoverService = new FailoverService(this); statisticsService = new StatisticsService(this); offsetService = new OffsetService(this); analyseService = new AnalyseService(this); limitMaxJobsService = new LimitMaxJobsService(this); listenerManager = new ListenerManager(this); reportService = new ReportService(this); // see EnabledPathListener and CronPathListener, only these values are supposed to be watched. previousConf.setTimeZone(jobConfiguration.getTimeZone()); previousConf.setCron(jobConfiguration.getCron()); previousConf.setPausePeriodDate(jobConfiguration.getPausePeriodDate()); previousConf.setPausePeriodTime(jobConfiguration.getPausePeriodTime()); previousConf.setProcessCountIntervalSeconds(jobConfiguration.getProcessCountIntervalSeconds()); } /** * 初始化作业. */ public void init() { try { String currentConfJobName = currentConf.getJobName(); log.info("[{}] msg=Elastic job: job controller init, job name is: {}.", jobName, currentConfJobName); // coordinatorRegistryCenter.addCacheData(JobNodePath.getJobNameFullPath(currentConfJobName)); startAll(); createJob(); serverService.persistServerOnline(); } catch (Throwable t) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, t.getMessage()), t); shutdown(false); } } private void startAll() { configService.start(); leaderElectionService.start(); serverService.start(); shardingService.start(); executionContextService.start(); executionService.start(); failoverService.start(); statisticsService.start(); offsetService.start(); limitMaxJobsService.start(); analyseService.start(); limitMaxJobsService.check(currentConf.getJobName()); listenerManager.start(); leaderElectionService.leaderElection(); serverService.clearRunOneTimePath(); serverService.clearStopOneTimePath(); serverService.resetCount(); statisticsService.startProcessCountJob(); } private void createJob() throws SchedulerException { Class<?> jobClass = currentConf.getSaturnJobClass(); try { job = (AbstractElasticJob) jobClass.newInstance(); } catch (Exception e) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, "createJobException:"), e); throw new RuntimeException("can not create job with job type " + currentConf.getJobType()); } job.setJobScheduler(this); job.setConfigService(configService); job.setShardingService(shardingService); job.setExecutionContextService(executionContextService); job.setExecutionService(executionService); job.setFailoverService(failoverService); job.setOffsetService(offsetService); job.setServerService(serverService); job.setExecutorName(executorName); job.setReportService(reportService); job.setJobName(jobName); job.setNamespace(coordinatorRegistryCenter.getNamespace()); job.setSaturnExecutorService(saturnExecutorService); job.init(); } private void initExecutorService() { ThreadFactory factory = new SaturnThreadFactory(jobName); executorService = new ExtendableThreadPoolExecutor(0, 100, 2, TimeUnit.MINUTES, new TaskQueue(), factory); } /** * 获取下次作业触发时间.可能被暂停时间段所影响。 * * @return 下次作业触发时间 */ public Date getNextFireTimePausePeriodEffected() { SaturnScheduler saturnScheduler = job.getScheduler(); if(saturnScheduler == null){ return null; } Trigger trigger = saturnScheduler.getTrigger(); if (trigger == null) { return null; } ((OperableTrigger) trigger).updateAfterMisfire(null); Date nextFireTime = trigger.getNextFireTime(); while (nextFireTime != null && configService.isInPausePeriod(nextFireTime)) { nextFireTime = trigger.getFireTimeAfter(nextFireTime); } if (null == nextFireTime) { return null; } return nextFireTime; } /** * 停止作业. * @param stopJob 是否强制停止作业 */ public void stopJob(boolean stopJob) { if (stopJob) { job.abort(); } else { job.stop(); } } /** * 恢复因服务器崩溃而停止的作业. * * <p> * 不会恢复手工设置停止运行的作业. * </p> */ /* * public void resumeCrashedJob() { serverService.persistServerOnline(); * executionService.clearRunningInfo(shardingService.getLocalHostShardingItems()); job.resume(); } */ /** * 立刻启动作业. */ public void triggerJob() { if (job.getScheduler().isShutdown()) { return; } job.getScheduler().triggerJob(); } /** * 关闭process count thread */ public void shutdownCountThread(){ statisticsService.shutdown(); } /** * 关闭调度器. */ public void shutdown(boolean removejob) { try { if (job != null) { job.shutdown(); Thread.sleep(500); } } catch (final Exception e) { log.error(String.format(SaturnConstant.ERROR_LOG_FORMAT, jobName, e.getMessage()), e); } listenerManager.shutdown(); shardingService.shutdown(); configService.shutdown(); leaderElectionService.shutdown(); serverService.shutdown(); executionContextService.shutdown(); executionService.shutdown(); failoverService.shutdown(); statisticsService.shutdown(); offsetService.shutdown(); analyseService.shutdown(); limitMaxJobsService.shutdown(); zkCacheManager.shutdown(); try { Thread.sleep(500); } catch (InterruptedException e) { } if (removejob) { jobNodeStorage.deleteJobNode(); saturnExecutorService.removeJobName(jobName); } JobRegistry.clearJob(executorName, jobName); if (executorService != null && !executorService.isShutdown()) { executorService.shutdown(); } } /** * 重新调度作业. * * @param cronExpression crom表达式 */ public void rescheduleJob(final String cronExpression) { try { if (job.getScheduler().isShutdown()) { return; } job.getTrigger().retrigger(job.getScheduler(), job); } catch (final SchedulerException ex) { throw new JobException(ex); } } /** * 重启统计处理数据数量的任务 */ public void rescheduleProcessCountJob() { statisticsService.startProcessCountJob(); } public String getJobName() { return jobName; } public void setJobName(String jobName) { this.jobName = jobName; } public String getExecutorName() { return executorName; } public void setExecutorName(String executorName) { this.executorName = executorName; } public JobConfiguration getPreviousConf() { return previousConf; } public void setPreviousConf(JobConfiguration previousConf) { this.previousConf = previousConf; } public AbstractElasticJob getJob() { return job; } public void setJob(AbstractElasticJob job) { this.job = job; } public SaturnExecutorService getSaturnExecutorService() { return saturnExecutorService; } public void setSaturnExecutorService(SaturnExecutorService saturnExecutorService) { this.saturnExecutorService = saturnExecutorService; } public JobConfiguration getCurrentConf() { return currentConf; } public CoordinatorRegistryCenter getCoordinatorRegistryCenter() { return coordinatorRegistryCenter; } public ListenerManager getListenerManager() { return listenerManager; } public ConfigurationService getConfigService() { return configService; } public ReportService getReportService() { return reportService; } public LeaderElectionService getLeaderElectionService() { return leaderElectionService; } public ServerService getServerService() { return serverService; } public ShardingService getShardingService() { return shardingService; } public ExecutionContextService getExecutionContextService() { return executionContextService; } public ExecutionService getExecutionService() { return executionService; } public FailoverService getFailoverService() { return failoverService; } public StatisticsService getStatisticsService() { return statisticsService; } public OffsetService getOffsetService() { return offsetService; } public AnalyseService getAnalyseService() { return analyseService; } public LimitMaxJobsService getLimitMaxJobsService() { return limitMaxJobsService; } public JobNodeStorage getJobNodeStorage() { return jobNodeStorage; } public ZkCacheManager getZkCacheManager() { return zkCacheManager; } public ExecutorService getExecutorService() { return executorService; } }