1. 程式人生 > >quartz 詳解篇

quartz 詳解篇

    一、Quartz 基本介紹

           1.1 Quartz 概述

           1.2 Quartz特點

           1.3 Quartz 叢集配置

    二、Quartz 原理及流程

           2.1 quartz基本原理

           2.2 quartz啟動流程

   三、Spring + Quartz 實現企業級排程的實現示例

          3.1 環境資訊

          3.2 相關程式碼及配置

    四、問題及解決方案

    五、相關知識

    六、參考資料

     總結

      

一、Quartz 基本介紹

    1.1 Quartz 概述

           Quartz 是 OpenSymphony 開源組織在任務排程領域的一個開源專案,完全基於 Java 實現。該專案於 2009 年被 Terracotta 收購,目前是 Terracotta 旗下的一個專案。讀者可以到 http://www.quartz-scheduler.org/站點下載 Quartz 的釋出版本及其原始碼。

   1.2 Quartz特點

       作為一個優秀的開源排程框架,Quartz 具有以下特點:

  1. 強大的排程功能,例如支援豐富多樣的排程方法,可以滿足各種常規及特殊需求;
  2. 靈活的應用方式,例如支援任務和排程的多種組合方式,支援排程資料的多種儲存方式;
  3. 分散式和叢集能力,Terracotta 收購後在原來功能基礎上作了進一步提升。

      另外,作為 Spring 預設的排程框架,Quartz 很容易與 Spring 整合實現靈活可配置的排程功能。

    quartz排程核心元素

  1. Scheduler:任務排程器,是實際執行任務排程的控制器。在spring中通過SchedulerFactoryBean封裝起來。
  2. Trigger:觸發器,用於定義任務排程的時間規則,有SimpleTrigger,CronTrigger,DateIntervalTrigger和NthIncludedDayTrigger,其中CronTrigger用的比較多,本文主要介紹這種方式。CronTrigger在spring中封裝在CronTriggerFactoryBean中。
  3. Calendar:它是一些日曆特定時間點的集合。一個trigger可以包含多個Calendar,以便排除或包含某些時間點。
  4. JobDetail:用來描述Job實現類及其它相關的靜態資訊,如Job名字、關聯監聽器等資訊。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean兩種實現,如果任務排程只需要執行某個類的某個方法,就可以通過MethodInvokingJobDetailFactoryBean來呼叫。
  5. Job:是一個介面,只有一個方法void execute(JobExecutionContext context),開發者實現該介面定義執行任務,JobExecutionContext類提供了排程上下文的各種資訊。Job執行時的資訊儲存在JobDataMap例項中。實現Job介面的任務,預設是無狀態的,若要將Job設定成有狀態的,在quartz中是給實現的Job新增@DisallowConcurrentExecution註解(以前是實現StatefulJob介面,現在已被Deprecated),在與spring結合中可以在spring配置檔案的job detail中配置concurrent引數。

  1.3 Quartz 叢集配置

     

quartz叢集是通過資料庫表來感知其他的應用的,各個節點之間並沒有直接的通訊。只有使用持久的JobStore才能完成Quartz叢集。
資料庫表:以前有12張表,現在只有11張表,現在沒有儲存listener相關的表,多了QRTZ_SIMPROP_TRIGGERS表:

Table name Description
QRTZ_CALENDARS 儲存Quartz的Calendar資訊
QRTZ_CRON_TRIGGERS 儲存CronTrigger,包括Cron表示式和時區資訊
QRTZ_FIRED_TRIGGERS 儲存與已觸發的Trigger相關的狀態資訊,以及相聯Job的執行資訊
QRTZ_PAUSED_TRIGGER_GRPS 儲存已暫停的Trigger組的資訊
QRTZ_SCHEDULER_STATE 儲存少量的有關Scheduler的狀態資訊,和別的Scheduler例項
QRTZ_LOCKS 儲存程式的悲觀鎖的資訊
QRTZ_JOB_DETAILS 儲存每一個已配置的Job的詳細資訊
QRTZ_SIMPLE_TRIGGERS 儲存簡單的Trigger,包括重複次數、間隔、以及已觸的次數
QRTZ_BLOG_TRIGGERS Trigger作為Blob型別儲存
QRTZ_TRIGGERS 儲存已配置的Trigger的資訊
QRTZ_SIMPROP_TRIGGERS  

QRTZ_LOCKS就是Quartz叢集實現同步機制的行鎖表,包括以下幾個鎖:CALENDAR_ACCESS 、JOB_ACCESS、MISFIRE_ACCESS 、STATE_ACCESS 、TRIGGER_ACCESS。

  二、Quartz 原理及流程

      2.1 quartz基本原理

         

核心元素

Quartz 任務排程的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任務排程的元資料, scheduler 是實際執行排程的控制器。

在 Quartz 中,trigger 是用於定義排程時間的元素,即按照什麼時間規則去執行任務。Quartz 中主要提供了四種類型的 trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和 NthIncludedDayTrigger。這四種 trigger 可以滿足企業應用中的絕大部分需求。我們將在企業應用一節中進一步討論四種 trigger 的功能。

在 Quartz 中,job 用於表示被排程的任務。主要有兩種型別的 job:無狀態的(stateless)和有狀態的(stateful)。對於同一個 trigger 來說,有狀態的 job 不能被並行執行,只有上一次觸發的任務被執行完之後,才能觸發下一次執行。Job 主要有兩種屬性:volatility 和 durability,其中 volatility 表示任務是否被持久化到資料庫儲存,而 durability 表示在沒有 trigger 關聯的時候任務是否被保留。兩者都是在值為 true 的時候任務被持久化或保留。一個 job 可以被多個 trigger 關聯,但是一個 trigger 只能關聯一個 job。

在 Quartz 中, scheduler 由 scheduler 工廠建立:DirectSchedulerFactory 或者 StdSchedulerFactory。 第二種工廠 StdSchedulerFactory 使用較多,因為 DirectSchedulerFactory 使用起來不夠方便,需要作許多詳細的手工編碼設定。 Scheduler 主要有三種:RemoteMBeanScheduler, RemoteScheduler 和 StdScheduler。本文以最常用的 StdScheduler 為例講解。這也是筆者在專案中所使用的 scheduler 類。

Quartz 核心元素之間的關係如下圖所示:

圖 1. Quartz 核心元素關係圖

圖 1. Quartz 核心元素關係圖

執行緒檢視

在 Quartz 中,有兩類執行緒,Scheduler 排程執行緒和任務執行執行緒,其中任務執行執行緒通常使用一個執行緒池維護一組執行緒。

圖 2. Quartz 執行緒檢視

圖 2. Quartz 執行緒檢視

Scheduler 排程執行緒主要有兩個: 執行常規排程的執行緒,和執行 misfired trigger 的執行緒。常規排程執行緒輪詢儲存的所有 trigger,如果有需要觸發的 trigger,即到達了下一次觸發的時間,則從任務執行執行緒池獲取一個空閒執行緒,執行與該 trigger 關聯的任務。Misfire 執行緒是掃描所有的 trigger,檢視是否有 misfired trigger,如果有的話根據 misfire 的策略分別處理。下圖描述了這兩個執行緒的基本流程:

圖 3. Quartz 排程執行緒流程圖

圖 3. Quartz 排程執行緒流程圖

關於 misfired trigger,我們在企業應用一節中將進一步描述。

資料儲存

Quartz 中的 trigger 和 job 需要儲存下來才能被使用。Quartz 中有兩種儲存方式:RAMJobStore, JobStoreSupport,其中 RAMJobStore 是將 trigger 和 job 儲存在記憶體中,而 JobStoreSupport 是基於 jdbc 將 trigger 和 job 儲存到資料庫中。RAMJobStore 的存取速度非常快,但是由於其在系統被停止後所有的資料都會丟失,所以在通常應用中,都是使用 JobStoreSupport。

在 Quartz 中,JobStoreSupport 使用一個驅動代理來操作 trigger 和 job 的資料儲存:StdJDBCDelegate。StdJDBCDelegate 實現了大部分基於標準 JDBC 的功能介面,但是對於各種資料庫來說,需要根據其具體實現的特點做某些特殊處理,因此各種資料庫需要擴充套件 StdJDBCDelegate 以實現這些特殊處理。Quartz 已經自帶了一些資料庫的擴充套件實現,可以直接使用,如下圖所示:

圖 4. Quartz 資料庫驅動代理

圖 4. Quartz 資料庫驅動代理

作為嵌入式資料庫的代表,Derby 近來非常流行。如果使用 Derby 資料庫,可以使用上圖中的 CloudscapeDelegate 作為 trigger 和 job 資料儲存的代理類。

   2.2 quartz啟動流程

   

若quartz是配置在spring中,當伺服器啟動時,就會裝載相關的bean。SchedulerFactoryBean實現了InitializingBean介面,因此在初始化bean的時候,會執行afterPropertiesSet方法,該方法將會呼叫SchedulerFactory(DirectSchedulerFactory 或者 StdSchedulerFactory,通常用StdSchedulerFactory)建立Scheduler。SchedulerFactory在建立quartzScheduler的過程中,將會讀取配置引數,初始化各個元件,關鍵元件如下:

  1. ThreadPool:一般是使用SimpleThreadPool,SimpleThreadPool建立了一定數量的WorkerThread例項來使得Job能夠線上程中進行處理。WorkerThread是定義在SimpleThreadPool類中的內部類,它實質上就是一個執行緒。在SimpleThreadPool中有三個list:workers-存放池中所有的執行緒引用,availWorkers-存放所有空閒的執行緒,busyWorkers-存放所有工作中的執行緒;
    執行緒池的配置引數如下所示:

    1
    2
    3
    org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount=3
    org.quartz.threadPool.threadPriority=5
            
    • 1
    • 2
  2. JobStore:分為儲存在記憶體的RAMJobStore和儲存在資料庫的JobStoreSupport(包括JobStoreTX和JobStoreCMT兩種實現,JobStoreCMT是依賴於容器來進行事務的管理,而JobStoreTX是自己管理事務),若要使用叢集要使用JobStoreSupport的方式;

  3. QuartzSchedulerThread:用來進行任務排程的執行緒,在初始化的時候paused=true,halted=false,雖然執行緒開始運行了,但是paused=true,執行緒會一直等待,直到start方法將paused置為false;

另外,SchedulerFactoryBean還實現了SmartLifeCycle介面,因此初始化完成後,會執行start()方法,該方法將主要會執行以下的幾個動作:

  1. 建立ClusterManager執行緒並啟動執行緒:該執行緒用來進行叢集故障檢測和處理,將在下文詳細討論;
  2. 建立MisfireHandler執行緒並啟動執行緒:該執行緒用來進行misfire任務的處理,將在下文詳細討論;
  3. 置QuartzSchedulerThread的paused=false,排程執行緒才真正開始排程;

整個啟動流程如下圖:
quartz啟動時序圖

 

 三、Spring + Quartz 實現企業級排程的實現示例

3.1 環境資訊

   此示例中的環境: Spring 4.1.6.RELEASE   + quartz 2.2.1 + Mysql 5.6

3.2 相關程式碼及配置

   3.2.1 Maven 引入

       

 3.2.2 資料庫指令碼準備

    

SET FOREIGN_KEY_CHECKS=0;

– —————————-
– Table structure for task_schedule_job
– —————————-
DROP TABLE IF EXISTS `task_schedule_job`;
CREATE TABLE `task_schedule_job` (
  `job_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `create_time` timestamp NULL DEFAULT NULL,
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `job_name` varchar(255) DEFAULT NULL,
  `job_group` varchar(255) DEFAULT NULL,
  `job_status` varchar(255) DEFAULT NULL,
  `cron_expression` varchar(255) NOT NULL,
  `description` varchar(255) DEFAULT NULL,
  `bean_class` varchar(255) DEFAULT NULL,
  `is_concurrent` varchar(255) DEFAULT NULL COMMENT ‘1’,
  `spring_id` varchar(255) DEFAULT NULL,
  `method_name` varchar(255) NOT NULL
  PRIMARY KEY (`job_id`),
  UNIQUE KEY `name_group` (`job_name`,`job_group`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

 

在Quartz包下docs/dbTables,選擇對應的資料庫指令碼,建立相應的資料庫表即可,我用的是mysql5.6,這裡有一個需要注意的地方,mysql5.5之前用的表儲存引擎是MyISAM,使用的是表級鎖,鎖發生衝突的概率比較高,併發度低;5.6之後預設的儲存引擎為InnoDB,InnoDB採用的鎖機制是行級鎖,併發度也較高。而quartz叢集使用資料庫鎖的

機制來來實現同一個任務在同一個時刻只被例項執行,所以為了防止衝突,我們建表的時候要選取InnoDB作為表的存

儲引擎。如下:

 

      

 3.2.3 關鍵程式碼及配置

   <1>spring-quartz.xml 配置 在application.xml 檔案中引入

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!-- 註冊本地排程任務
    <bean id="localQuartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"></bean>-->

    <!-- 註冊叢集排程任務 -->
    <bean id="schedulerFactoryBean" lazy-init="false" autowire="no"
          class="org.springframework.scheduling.quartz.SchedulerFactoryBean" destroy-method="destroy">
        <!--可選,QuartzScheduler 啟動時更新己存在的Job,這樣就不用每次修改targetObject後刪除qrtz_job_details表對應記錄了 -->
        <property name="overwriteExistingJobs" value="true" />
        <!--必須的,QuartzScheduler 延時啟動,應用啟動完後 QuartzScheduler 再啟動 -->
        <property name="startupDelay" value="3" />
        <!-- 設定自動啟動 -->
        <property name="autoStartup" value="true" />
        <property name="applicationContextSchedulerContextKey" value="applicationContext" />
        <property name="configLocation" value="classpath:quartz.properties" />
    </bean>

</beans>

<2>quartz.properties 檔案配置

#==============================================================
#Configure Main Scheduler Properties
#==============================================================
org.quartz.scheduler.instanceName = KuanrfGSQuartzScheduler
org.quartz.scheduler.instanceId = AUTO

#==============================================================
#Configure JobStore
#==============================================================
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.maxMisfiresToHandleAtATime = 1
org.quartz.jobStore.misfireThreshold = 120000
org.quartz.jobStore.txIsolationLevelSerializable = false

#==============================================================
#Configure DataSource
#==============================================================
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = 你的資料鏈接
org.quartz.dataSource.myDS.user = 使用者名稱
org.quartz.dataSource.myDS.password = 密碼
org.quartz.dataSource.myDS.maxConnections = 30
org.quartz.jobStore.selectWithLockSQL = SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE

#==============================================================
#Configure ThreadPool
#==============================================================
org.quartz.threadPool.class= org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount= 10
org.quartz.threadPool.threadPriority= 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread= true

#==============================================================
#Skip Check Update
#update:true
#not update:false
#==============================================================
org.quartz.scheduler.skipUpdateCheck = true

#============================================================================
# Configure Plugins
#============================================================================
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingJobHistoryPlugin
org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin
org.quartz.plugin.shutdownhook.cleanShutdown = true
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

<3>關鍵程式碼

package com.netease.ad.omp.service.sys;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import com.netease.ad.omp.common.utils.SpringUtils;
import com.netease.ad.omp.dao.sys.mapper.ScheduleJobMapper;
import com.netease.ad.omp.entity.sys.ScheduleJob;
import com.netease.ad.omp.quartz.job.JobUtils;
import com.netease.ad.omp.quartz.job.MyDetailQuartzJobBean;
import com.netease.ad.omp.quartz.job.QuartzJobFactory;
import com.netease.ad.omp.quartz.job.QuartzJobFactoryDisallowConcurrentExecution;
import org.apache.log4j.Logger;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;


/**
 * 計劃任務管理
 */
@Service
public class JobTaskService {
   public final Logger log = Logger.getLogger(this.getClass());
   @Autowired
   private SchedulerFactoryBean schedulerFactoryBean;

   @Autowired
   private ScheduleJobMapper scheduleJobMapper;


   /**
    * 從資料庫中取 區別於getAllJob
    * 
    * @return
    */
   public List<ScheduleJob> getAllTask() {
      return scheduleJobMapper.select(null);
   }

   /**
    * 新增到資料庫中 區別於addJob
    */
   public void addTask(ScheduleJob job) {
      job.setCreateTime(new Date());
      scheduleJobMapper.insertSelective(job);
   }

   /**
    * 從資料庫中查詢job
    */
   public ScheduleJob getTaskById(Long jobId) {
      return scheduleJobMapper.selectByPrimaryKey(jobId);
   }

   /**
    * 更改任務狀態
    * 
    * @throws SchedulerException
    */
   public void changeStatus(Long jobId, String cmd) throws SchedulerException {
      ScheduleJob job = getTaskById(jobId);
      if (job == null) {
         return;
      }
      if ("stop".equals(cmd)) {
         deleteJob(job);
         job.setJobStatus(JobUtils.STATUS_NOT_RUNNING);
      } else if ("start".equals(cmd)) {
         job.setJobStatus(JobUtils.STATUS_RUNNING);
         addJob(job);
      }
      scheduleJobMapper.updateByPrimaryKeySelective(job);
   }

   /**
    * 更改任務 cron表示式
    * 
    * @throws SchedulerException
    */
   public void updateCron(Long jobId, String cron) throws SchedulerException {
      ScheduleJob job = getTaskById(jobId);
      if (job == null) {
         return;
      }
      job.setCronExpression(cron);
      if (JobUtils.STATUS_RUNNING.equals(job.getJobStatus())) {
         updateJobCron(job);
      }
      scheduleJobMapper.updateByPrimaryKeySelective(job);
   }

   /**
    * 新增任務
    * 
    * @throws SchedulerException
    */
   public void addJob(ScheduleJob job) throws SchedulerException {
      if (job == null || !JobUtils.STATUS_RUNNING.equals(job.getJobStatus())) {
         return;
      }
      Scheduler scheduler = schedulerFactoryBean.getScheduler();
      log.debug(scheduler + ".......................................................................................add");
      TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());

      CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

      // 不存在,建立一個
      if (null == trigger) {
         Class clazz = JobUtils.CONCURRENT_IS.equals(job.getIsConcurrent()) ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;
         JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();

         jobDetail.getJobDataMap().put("scheduleJob", job);

         CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

         trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();

         scheduler.scheduleJob(jobDetail, trigger);
      } else {
         // Trigger已存在,那麼更新相應的定時設定
         CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

         // 按新的cronExpression表示式重新構建trigger
         trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

         // 按新的trigger重新設定job執行
         scheduler.rescheduleJob(triggerKey, trigger);
      }
   }

   @PostConstruct
   public void init() throws Exception {

      // 這裡獲取任務資訊資料
      List<ScheduleJob> jobList = scheduleJobMapper.select(null);

      for (ScheduleJob job : jobList) {
         addJob(job);
      }
   }

   /**
    * 獲取所有計劃中的任務列表
    * 
    * @return
    * @throws SchedulerException
    */
   public List<ScheduleJob> getAllJob() throws SchedulerException {
      Scheduler scheduler = schedulerFactoryBean.getScheduler();
      GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
      Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
      List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
      for (JobKey jobKey : jobKeys) {
         List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
         for (Trigger trigger : triggers) {
            ScheduleJob job = new ScheduleJob();
            job.setJobName(jobKey.getName());
            job.setJobGroup(jobKey.getGroup());
            job.setDescription("觸發器:" + trigger.getKey());
            Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
            job.setJobStatus(triggerState.name());
            if (trigger instanceof CronTrigger) {
               CronTrigger cronTrigger = (CronTrigger) trigger;
               String cronExpression = cronTrigger.getCronExpression();
               job.setCronExpression(cronExpression);
            }
            jobList.add(job);
         }
      }
      return jobList;
   }

   /**
    * 所有正在執行的job
    * 
    * @return
    * @throws SchedulerException
    */
   public List<ScheduleJob> getRunningJob() throws SchedulerException {
      Scheduler scheduler = schedulerFactoryBean.getScheduler();
      List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
      List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
      for (JobExecutionContext executingJob : executingJobs) {
         ScheduleJob job = new ScheduleJob();
         JobDetail jobDetail = executingJob.getJobDetail();
         JobKey jobKey = jobDetail.getKey();
         Trigger trigger = executingJob.getTrigger();
         job.setJobName(jobKey.getName());
         job.setJobGroup(jobKey.getGroup());
         job.setDescription("觸發器:" + trigger.getKey());
         Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
         job.setJobStatus(triggerState.name());
         if (trigger instanceof CronTrigger) {
            CronTrigger cronTrigger = (CronTrigger) trigger;
            String cronExpression = cronTrigger.getCronExpression();
            job.setCronExpression(cronExpression);
         }
         jobList.add(job);
      }
      return jobList;
   }

   /**
    * 暫停一個job
    * 
    * @param scheduleJob
    * @throws SchedulerException
    */
   public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
      Scheduler scheduler = schedulerFactoryBean.getScheduler();
      JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
      scheduler.pauseJob(jobKey);
   }

   /**
    * 恢復一個job
    * 
    * @param scheduleJob
    * @throws SchedulerException
    */
   public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
      Scheduler scheduler = schedulerFactoryBean.getScheduler();
      JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
      scheduler.resumeJob(jobKey);
   }

   /**
    * 刪除一個job
    * 
    * @param scheduleJob
    * @throws SchedulerException
    */
   public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
      Scheduler scheduler = schedulerFactoryBean.getScheduler();
      JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
      scheduler.deleteJob(jobKey);

   }

   /**
    * 立即執行job
    * 
    * @param scheduleJob
    * @throws SchedulerException
    */
   public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
      Scheduler scheduler = schedulerFactoryBean.getScheduler();
      JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
      scheduler.triggerJob(jobKey);
   }

   /**
    * 更新job時間表達式
    * 
    * @param scheduleJob
    * @throws SchedulerException
    */
   public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
      Scheduler scheduler = schedulerFactoryBean.getScheduler();

      TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());

      CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

      CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression());

      trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

      scheduler.rescheduleJob(triggerKey, trigger);
   }

   public static void main(String[] args) {
      CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("xxxxx");
   }
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
package com.netease.ad.omp.quartz.job;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.netease.ad.omp.common.utils.SpringUtils;
import com.netease.ad.omp.entity.sys.ScheduleJob;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.quartz.JobExecutionContext;
import org.springframework.context.ApplicationContext;

/**
 * Created with IntelliJ IDEA
 * ProjectName: omp
 * Author:  bjsonghongxu
 * CreateTime : 15:58
 * Email: [email protected]
 * Class Description:
 *   定時任務工具類
 */
public class JobUtils {
    public final static Logger log = Logger.getLogger(JobUtils.class);
    public static final String STATUS_RUNNING = "1"; //啟動狀態
    public static final String STATUS_NOT_RUNNING = "0"; //未啟動狀態
    public static final String CONCURRENT_IS = "1";
    public static final String CONCURRENT_NOT = "0";

    private ApplicationContext ctx;

    /**
     * 通過反射呼叫scheduleJob中定義的方法
     *
     * @param scheduleJob
     */
    public static void invokMethod(ScheduleJob scheduleJob,JobExecutionContext context) {
        Object object = null;
        Class clazz = null;
        if (StringUtils.isNotBlank(scheduleJob.getSpringId())) {
            object = SpringUtils.getBean(scheduleJob.getSpringId());
        } else if (StringUtils.isNotBlank(scheduleJob.getBeanClass())) {
            try {
                clazz = Class.forName(scheduleJob.getBeanClass());
                object = clazz.newInstance();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
        if (object == null) {
            log.error("任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啟動成功,請檢查是否配置正確!!!");
            return;
        }
        clazz = object.getClass();
        Method method = null;
        try {
            method = clazz.getMethod(scheduleJob.getMethodName(), new Class[] {JobExecutionContext.class});
        } catch (NoSuchMethodException e) {
            log.error("任務名稱 = [" + scheduleJob.getJobName() + "]---------------未啟動成功,方法名設定錯誤!!!");
        } catch (SecurityException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (method != null) {
            try {
                method.invoke(object, new Object[] {context});
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        log.info("任務名稱 = [" + scheduleJob.getJobName() + "]----------啟動成功");
    }
}
package com.netease.ad.omp.quartz.job;

import com.netease.ad.omp.entity.sys.ScheduleJob;
import org.apache.log4j.Logger;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;


/**
 * 
 * @Description: 計劃任務執行處 無狀態
 *  Spring排程任務 (重寫 quartz 的 QuartzJobBean 類原因是在使用 quartz+spring 把 quartz 的 task 例項化進入資料庫時,會產生: serializable 的錯誤)
 */
public class QuartzJobFactory implements Job {
   public final Logger log = Logger.getLogger(this.getClass());

   @Override
   public void execute(JobExecutionContext context) throws JobExecutionException {
      ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
      JobUtils.invokMethod(scheduleJob,context);
   }
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
package com.netease.ad.omp.quartz.job;

import com.netease.ad.omp.entity.sys.ScheduleJob;
import org.apache.log4j.Logger;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;


/**
 * 
 * @Description: 若一個方法一次執行不完下次輪轉時則等待該方法執行完後才執行下一次操作
 *  Spring排程任務 (重寫 quartz 的 QuartzJobBean 類原因是在使用 quartz+spring 把 quartz 的 task 例項化進入資料庫時,會產生: serializable 的錯誤)
 */
@DisallowConcurrentExecution
public class QuartzJobFactoryDisallowConcurrentExecution implements Job {
   public final Logger log = Logger.getLogger(this.getClass());

   @Override
   public void execute(JobExecutionContext context) throws JobExecutionException {
      ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap().get("scheduleJob");
      JobUtils.invokMethod(scheduleJob,context);

   }
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

 

package com.netease.ad.omp.entity.sys;

import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Date;

/**
 * Created with IntelliJ IDEA
 * ProjectName: omp
 * Author:  bjsonghongxu
 * CreateTime : 15:48
 * Email: [email protected]
 * Class Description:
 *   計劃任務資訊
 */
@Table(name = "task_schedule_job")
public class ScheduleJob implements Serializable {
    @Id
    private Long jobId;

    private Date createTime;

    private Date updateTime;
    /**
     * 任務名稱
     */
    private String jobName;
    /**
     * 任務分組
     */
    private String jobGroup;
    /**
     * 任務狀態 是否啟動任務
     */
    private String jobStatus;
    /**
     * cron表示式
     */
    private String cronExpression;
    /**
     * 描述
     */
    private String description;
    /**
     * 任務執行時呼叫哪個類的方法 包名+類名
     */
    private String beanClass;
    /**
     * 任務是否有狀態
     */
    private String isConcurrent;
    /**
     * spring bean
     */
    private String springId;
    /**
     * 任務呼叫的方法名
     */
    private String methodName;

    public Long getJobId() {
        return jobId;
    }

    public void setJobId(Long jobId) {
        this.jobId = jobId;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    public String getJobName() {
        return jobName;
    }

    public void setJobName(String jobName) {
        this.jobName = jobName;
    }

    public String getJobGroup() {
        return jobGroup;
    }

    public void setJobGroup(String jobGroup) {
        this.jobGroup = jobGroup;
    }

    public String getJobStatus() {
        return jobStatus;
    }

    public void setJobStatus(String jobStatus) {
        this.jobStatus = jobStatus;
    }

    public String getCronExpression() {
        return cronExpression;
    }

    public void setCronExpression(String cronExpression) {
        this.cronExpression = cronExpression;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getBeanClass() {
        return beanClass;
    }

    public void setBeanClass(String beanClass) {
        this.beanClass = beanClass;
    }

    public String getIsConcurrent() {
        return isConcurrent;
    }

    public void setIsConcurrent(String isConcurrent) {
        this.isConcurrent = isConcurrent;
    }

    public String getSpringId() {
        return springId;
    }

    public void setSpringId(String springId) {
        this.springId = springId;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
}
package com.netease.ad.omp.common.utils;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public final class SpringUtils implements 
            
           

相關推薦

quartz

    一、Quartz 基本介紹            1.1 Quartz 概述            

quartz (從原理到應用)

一、Quartz 基本介紹           1.1 Quartz 概述           1.2 Quartz特點           1.3 Quartz 叢集配置    二、Quartz 原理及流程           2.1 quartz基本原理         

Python列表list(七)

python list 介紹:列表是最常用的python數據類型,它可以作為一個方括號內的逗號用分隔值出現。列表的數據項不需要具有相同的類型。創建一個列表,只要逗號分隔的不同數據項用方括號括起來即可。name=[‘’beijing,’shenzhen’,’nanjing’] 訪問列表的值:(列表的下標

quartz2:quartz由淺入深

adc 伸縮 execute path 頻繁 it168 cmt logs 展示 http://blog.itpub.net/11627468/viewspace-1763498/ 一、quartz核心概念 先來看一張圖: scheduler

springboot 整合quartz

本文主要是針對springboot 整合quartz 來說明,作為一個小白我總喜歡上網查詢資料,然後希望找的東西能80%符合自己的需求,能直接拿來用的,但是很多時候網上的案例都是一個copy一個的,看的東西千篇一律,而且有些內容解釋的也是寥寥數語,所以有時候也需要自己寫點東西分享給大眾,今天也是自己

Spring之IoC控制反轉

###一.Spring概況 spring是一個開源框架 是一個輕量的控制反轉和麵向切面的容器框架 大小和開銷都是輕量的。 通過控制反轉技術可以達到鬆耦合的目的 切面程式設計,允許通過分離應用的業務邏輯。 包含並管理應用物件的配置和生命週期,是一個容器,並且能夠組

Quartz定時任務框架(二) Quartz

目錄 Quartz API Scheduler排程程式、SchedulerFactory排程程式工廠 scheduler排程程式 SchedulerFactory Job & JobDetail JobDataMap Job例項化的過程 job的註解宣告和併發

quartz5-quartz與spring結合

1. spring中quartz依賴 2. quartz配置作業的兩種方式及配置檔案 慕課網_《Java定時任務排程工具詳解之Quartz篇》學習總結 1. spring中quartz依賴

quartz4-Scheduler與屬性配置

1. Scheduler 工廠模式 SchedulerFactory 有兩個實現類,最常用的是StdSchedulerFactory,宣告式、配置式 (另一個實現類程式碼式已不用) SchedulerFactory | | Sched

Java實現的SFTP(檔案上傳

JSch是Java Secure Channel的縮寫。JSch是一個SSH2的純Java實現。它允許你連線到一個SSH伺服器,並且可以使用埠轉發,X11轉發,檔案傳輸等,當然你也可以整合它的功能到你自己的應用程式。 本文只介紹如何使用JSch實現的SFTP功能

Spring之 AOP面向切面程式設計

Aop(aspect oriented programming面向切面程式設計),是spring框架的另一個特徵。AOP包括切面、連線點、通知(advice)、切入點(pointCut) 。 1.aop幾個概念: 橫切關注點: 對哪些方面進行攔截,攔截後怎麼處理。 切面

Java實現的SFTP(檔案下載

上一篇講述了使用JSch實現檔案上傳的功能,這一篇主要講述一下JSch實現檔案下載的功能。並介紹一些SFTP的輔助方法,如cd,ls等。 同樣,JSch的檔案下載也支援三種傳輸模式:OVERWRITE, RESUME和APPEND,請參考上篇隨筆:JSch - Ja

python基礎大合集,程序、裝飾器、列表

程序以及狀態 1. 程序 2. 程序的狀態 程序的建立-multiprocessing 1. 建立程序 2. 程序pid 3. Process語法結構如下 4. 給子程序指定的函式傳遞引數 5. 程序間不共享全域性變數 程序和執行緒對比

【SpringMVC】SpringMVC初學

      SpringMVC是一個基於MVC的Web框架,是spring框架的一個模組,使用了MVC架構模式的思想,將web層進行職責解耦。首先讓我們整體看一下SpringMVC處理請求的流程: 發起請求到前端控制器(DispatcherSe

華芸ADM3.2 二:ASUSTOR 華芸 NAS的多重檔案保護系統設定和遠端連線

原文網址:https://post.smzdm.com/p/a07mrpn9/       上一篇我對華芸的AS6404T進行了開箱,並對ADM3.2進行了簡析,本篇我將對華芸ADM3.2的多重檔案保護系統設定和遠端連線進行詳解,首先,先進行EZ-Conn

華芸ADM3.2 一:華芸NAS——AS6404T開箱,ADM3.2簡析

原文網址:https://post.smzdm.com/p/778533/       經常關注我測評的人都知道我是個尤其喜歡玩各種儲存裝置和NAS的人,這次我要分享的是華芸的AS6404T,這是我第一次用華芸的NAS,和以往我分享的NAS不一樣的是640

每天五個java相關面試題(9)--java基礎1

接下來會系統的總結java基礎,然後過兩天會開始從新開始複習前端並更新和總結一些關於前端的基礎知識和麵試題,嗯嗯我是一個想走前端的後端java工程師啊哈哈。馬上開學大四了,lz我要加油啦~ 接下來的面試題會學習我們班小夥伴的好學習方法,一個個刨根問底了,理清楚

Quartz

目錄:       一、Quartz 基本介紹            1.1 Quartz 概述            1.2&

Intellij IDEA 2017 debug斷點除錯技巧與總結

在除錯程式碼的時候,你的專案得debug模式啟動,也就是點那個綠色的甲蟲啟動伺服器,然後,就可以在程式碼裡面斷點除錯啦。 下面不要在意,這個快捷鍵具體是啥,因為,這個keymap是可以自己配置的,有的人keymap是mac版的,有的是Windows版的。 我的就是Windows,而且修改keymap為

Android Glide Google 推薦載入圖片框架(載入圖片

每個時間,都會有不同的心情,學會享受現在的心情,無論快樂亦或悲傷的心情,都是生活的點滴印記。 看到這個,你一定會說,我現在在用ImageLoader,Picasso,Fresco或其它框架中的圖片載入,都挺好用的,為什麼要選用Glide呢? 答