欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Quartz集群原理以及配置應(yīng)用的方法詳解

 更新時(shí)間:2018年05月07日 09:43:57   作者:振宇要低調(diào)  
Quartz是Java領(lǐng)域最著名的開(kāi)源任務(wù)調(diào)度工具。Quartz提供了極為廣泛的特性如持久化任務(wù),集群和分布式任務(wù)等,下面這篇文章主要給大家介紹了關(guān)于Quartz集群原理以及配置應(yīng)用的相關(guān)資料,需要的朋友可以參考下

1、Quartz任務(wù)調(diào)度的基本實(shí)現(xiàn)原理

  Quartz是OpenSymphony開(kāi)源組織在任務(wù)調(diào)度領(lǐng)域的一個(gè)開(kāi)源項(xiàng)目,完全基于Java實(shí)現(xiàn)。作為一個(gè)優(yōu)秀的開(kāi)源調(diào)度框架,Quartz具有以下特點(diǎn):

   ?。?)強(qiáng)大的調(diào)度功能,例如支持豐富多樣的調(diào)度方法,可以滿(mǎn)足各種常規(guī)及特殊需求;

   ?。?)靈活的應(yīng)用方式,例如支持任務(wù)和調(diào)度的多種組合方式,支持調(diào)度數(shù)據(jù)的多種存儲(chǔ)方式;

   ?。?)分布式和集群能力,Terracotta收購(gòu)后在原來(lái)功能基礎(chǔ)上作了進(jìn)一步提升。本文將對(duì)該部分相加闡述。

1.1 Quartz 核心元素

  Quartz任務(wù)調(diào)度的核心元素為:Scheduler——任務(wù)調(diào)度器、Trigger——觸發(fā)器、Job——任務(wù)。其中trigger和job是任務(wù)調(diào)度的元數(shù)據(jù),scheduler是實(shí)際執(zhí)行調(diào)度的控制器。

  Trigger是用于定義調(diào)度時(shí)間的元素,即按照什么時(shí)間規(guī)則去執(zhí)行任務(wù)。Quartz中主要提供了四種類(lèi)型的trigger:SimpleTrigger,CronTirgger,DateIntervalTrigger,和NthIncludedDayTrigger。這四種trigger可以滿(mǎn)足企業(yè)應(yīng)用中的絕大部分需求。

  Job用于表示被調(diào)度的任務(wù)。主要有兩種類(lèi)型的job:無(wú)狀態(tài)的(stateless)和有狀態(tài)的(stateful)。對(duì)于同一個(gè)trigger來(lái)說(shuō),有狀態(tài)的job不能被并行執(zhí)行,只有上一次觸發(fā)的任務(wù)被執(zhí)行完之后,才能觸發(fā)下一次執(zhí)行。Job主要有兩種屬性:volatility和durability,其中volatility表示任務(wù)是否被持久化到數(shù)據(jù)庫(kù)存儲(chǔ),而durability表示在沒(méi)有trigger關(guān)聯(lián)的時(shí)候任務(wù)是否被保留。兩者都是在值為true的時(shí)候任務(wù)被持久化或保留。一個(gè)job可以被多個(gè)trigger關(guān)聯(lián),但是一個(gè)trigger只能關(guān)聯(lián)一個(gè)job。

  Scheduler由scheduler工廠(chǎng)創(chuàng)建:DirectSchedulerFactory或者StdSchedulerFactory。第二種工廠(chǎng)StdSchedulerFactory使用較多,因?yàn)镈irectSchedulerFactory使用起來(lái)不夠方便,需要作許多詳細(xì)的手工編碼設(shè)置。Scheduler主要有三種:RemoteMBeanScheduler,RemoteScheduler和StdScheduler。

  Quartz核心元素之間的關(guān)系如圖1.1所示:

圖1.1 核心元素關(guān)系圖

1.2 Quartz 線(xiàn)程視圖

  在Quartz中,有兩類(lèi)線(xiàn)程,Scheduler調(diào)度線(xiàn)程和任務(wù)執(zhí)行線(xiàn)程,其中任務(wù)執(zhí)行線(xiàn)程通常使用一個(gè)線(xiàn)程池維護(hù)一組線(xiàn)程。

圖1.2 Quartz線(xiàn)程視圖

  Scheduler調(diào)度線(xiàn)程主要有兩個(gè):執(zhí)行常規(guī)調(diào)度的線(xiàn)程,和執(zhí)行misfiredtrigger的線(xiàn)程。常規(guī)調(diào)度線(xiàn)程輪詢(xún)存儲(chǔ)的所有trigger,如果有需要觸發(fā)的trigger,即到達(dá)了下一次觸發(fā)的時(shí)間,則從任務(wù)執(zhí)行線(xiàn)程池獲取一個(gè)空閑線(xiàn)程,執(zhí)行與該trigger關(guān)聯(lián)的任務(wù)。Misfire線(xiàn)程是掃描所有的trigger,查看是否有misfiredtrigger,如果有的話(huà)根據(jù)misfire的策略分別處理(fire now OR wait for the next fire)。

1.3 Quartz Job數(shù)據(jù)存儲(chǔ)

  Quartz中的trigger和job需要存儲(chǔ)下來(lái)才能被使用。Quartz中有兩種存儲(chǔ)方式:RAMJobStore,JobStoreSupport,其中RAMJobStore是將trigger和job存儲(chǔ)在內(nèi)存中,而JobStoreSupport是基于jdbc將trigger和job存儲(chǔ)到數(shù)據(jù)庫(kù)中。RAMJobStore的存取速度非??欤怯捎谄湓谙到y(tǒng)被停止后所有的數(shù)據(jù)都會(huì)丟失,所以在集群應(yīng)用中,必須使用JobStoreSupport。

2、Quartz集群原理2.1 Quartz 集群架構(gòu)

  一個(gè)Quartz集群中的每個(gè)節(jié)點(diǎn)是一個(gè)獨(dú)立的Quartz應(yīng)用,它又管理著其他的節(jié)點(diǎn)。這就意味著你必須對(duì)每個(gè)節(jié)點(diǎn)分別啟動(dòng)或停止。Quartz集群中,獨(dú)立的Quartz節(jié)點(diǎn)并不與另一其的節(jié)點(diǎn)或是管理節(jié)點(diǎn)通信,而是通過(guò)相同的數(shù)據(jù)庫(kù)表來(lái)感知到另一Quartz應(yīng)用的,如圖2.1所示。

圖2.1 Quartz集群架構(gòu)

2.2 Quartz集群相關(guān)數(shù)據(jù)庫(kù)表

  因?yàn)镼uartz集群依賴(lài)于數(shù)據(jù)庫(kù),所以必須首先創(chuàng)建Quartz數(shù)據(jù)庫(kù)表,Quartz發(fā)布包中包括了所有被支持的數(shù)據(jù)庫(kù)平臺(tái)的SQL腳本。這些SQL腳本存放于<quartz_home>/docs/dbTables 目錄下。這里采用的Quartz 1.8.4版本,總共12張表,不同版本,表個(gè)數(shù)可能不同。數(shù)據(jù)庫(kù)為mysql,用tables_mysql.sql創(chuàng)建數(shù)據(jù)庫(kù)表。全部表如圖2.2所示,對(duì)這些表的簡(jiǎn)要介紹如圖2.3所示。

圖2.2 Quartz 1.8.4在mysql數(shù)據(jù)庫(kù)中生成的表

圖2.3 Quartz數(shù)據(jù)表簡(jiǎn)介

2.2.1 調(diào)度器狀態(tài)表(QRTZ_SCHEDULER_STATE)

  說(shuō)明:集群中節(jié)點(diǎn)實(shí)例信息,Quartz定時(shí)讀取該表的信息判斷集群中每個(gè)實(shí)例的當(dāng)前狀態(tài)。

  instance_name:配置文件中org.quartz.scheduler.instanceId配置的名字,如果設(shè)置為AUTO,quartz會(huì)根據(jù)物理機(jī)名和當(dāng)前時(shí)間產(chǎn)生一個(gè)名字。

  last_checkin_time:上次檢入時(shí)間

  checkin_interval:檢入間隔時(shí)間

2.2.2 觸發(fā)器與任務(wù)關(guān)聯(lián)表(qrtz_fired_triggers)

  存儲(chǔ)與已觸發(fā)的Trigger相關(guān)的狀態(tài)信息,以及相聯(lián)Job的執(zhí)行信息。

2.2.3 觸發(fā)器信息表(qrtz_triggers)

  trigger_name:trigger的名字,該名字用戶(hù)自己可以隨意定制,無(wú)強(qiáng)行要求

  trigger_group:trigger所屬組的名字,該名字用戶(hù)自己隨意定制,無(wú)強(qiáng)行要求

  job_name:qrtz_job_details表job_name的外鍵

  job_group:qrtz_job_details表job_group的外鍵

  trigger_state:當(dāng)前trigger狀態(tài)設(shè)置為ACQUIRED,如果設(shè)為WAITING,則job不會(huì)觸發(fā)

  trigger_cron:觸發(fā)器類(lèi)型,使用cron表達(dá)式

2.2.4 任務(wù)詳細(xì)信息表(qrtz_job_details)

  說(shuō)明:保存job詳細(xì)信息,該表需要用戶(hù)根據(jù)實(shí)際情況初始化

  job_name:集群中job的名字,該名字用戶(hù)自己可以隨意定制,無(wú)強(qiáng)行要求。

  job_group:集群中job的所屬組的名字,該名字用戶(hù)自己隨意定制,無(wú)強(qiáng)行要求。

  job_class_name:集群中job實(shí)現(xiàn)類(lèi)的完全包名,quartz就是根據(jù)這個(gè)路徑到classpath找到該job類(lèi)的。

  is_durable:是否持久化,把該屬性設(shè)置為1,quartz會(huì)把job持久化到數(shù)據(jù)庫(kù)中

  job_data:一個(gè)blob字段,存放持久化job對(duì)象。

2.2.5權(quán)限信息表(qrtz_locks)

  說(shuō)明:tables_oracle.sql里有相應(yīng)的dml初始化,如圖2.4所示。

圖2.4 Quartz權(quán)限信息表中的初始化信息

2.3 Quartz Scheduler在集群中的啟動(dòng)流程

  Quartz Scheduler自身是察覺(jué)不到被集群的,只有配置給Scheduler的JDBC JobStore才知道。當(dāng)Quartz Scheduler啟動(dòng)時(shí),它調(diào)用JobStore的schedulerStarted()方法,它告訴JobStore Scheduler已經(jīng)啟動(dòng)了。schedulerStarted() 方法是在JobStoreSupport類(lèi)中實(shí)現(xiàn)的。JobStoreSupport類(lèi)會(huì)根據(jù)quartz.properties文件中的設(shè)置來(lái)確定Scheduler實(shí)例是否參與到集群中。假如配置了集群,一個(gè)新的ClusterManager類(lèi)的實(shí)例就被創(chuàng)建、初始化并啟動(dòng)。ClusterManager是在JobStoreSupport類(lèi)中的一個(gè)內(nèi)嵌類(lèi),繼承了java.lang.Thread,它會(huì)定期運(yùn)行,并對(duì)Scheduler實(shí)例執(zhí)行檢入的功能。Scheduler也要查看是否有任何一個(gè)別的集群節(jié)點(diǎn)失敗了。檢入操作執(zhí)行周期在quartz.properties中配置。

2.4 偵測(cè)失敗的Scheduler節(jié)點(diǎn)

  當(dāng)一個(gè)Scheduler實(shí)例執(zhí)行檢入時(shí),它會(huì)查看是否有其他的Scheduler實(shí)例在到達(dá)他們所預(yù)期的時(shí)間還未檢入。這是通過(guò)檢查SCHEDULER_STATE表中Scheduler記錄在LAST_CHEDK_TIME列的值是否早于org.quartz.jobStore.clusterCheckinInterval來(lái)確定的。如果一個(gè)或多個(gè)節(jié)點(diǎn)到了預(yù)定時(shí)間還沒(méi)有檢入,那么運(yùn)行中的Scheduler就假定它(們) 失敗了。

2.5 從故障實(shí)例中恢復(fù)Job

  當(dāng)一個(gè)Sheduler實(shí)例在執(zhí)行某個(gè)Job時(shí)失敗了,有可能由另一正常工作的Scheduler實(shí)例接過(guò)這個(gè)Job重新運(yùn)行。要實(shí)現(xiàn)這種行為,配置給JobDetail對(duì)象的Job可恢復(fù)屬性必須設(shè)置為true(job.setRequestsRecovery(true))。如果可恢復(fù)屬性被設(shè)置為false(默認(rèn)為false),當(dāng)某個(gè)Scheduler在運(yùn)行該job失敗時(shí),它將不會(huì)重新運(yùn)行;而是由另一個(gè)Scheduler實(shí)例在下一次觸發(fā)時(shí)間觸發(fā)。Scheduler實(shí)例出現(xiàn)故障后多快能被偵測(cè)到取決于每個(gè)Scheduler的檢入間隔(即2.3中提到的org.quartz.jobStore.clusterCheckinInterval)。

3、Quartz集群實(shí)例(Quartz+Spring)

3.1 Spring不兼容Quartz問(wèn)題

  Spring從2.0.2開(kāi)始便不再支持Quartz。具體表現(xiàn)在Quartz+Spring把Quartz的Task實(shí)例化進(jìn)入數(shù)據(jù)庫(kù)時(shí),會(huì)產(chǎn)生:Serializable的錯(cuò)誤:

<bean id="jobtask" class="org.springframework.scheduling.quartz. MethodInvokingJobDetailFactoryBean ">
  <property name="targetObject">
    <ref bean="quartzJob"/>
  </property>
  <property name="targetMethod">
    <value>execute</value>
  </property>
</bean>

  這個(gè)MethodInvokingJobDetailFactoryBean類(lèi)中的methodInvoking方法,是不支持序列化的,因此在把QUARTZ的TASK序列化進(jìn)入數(shù)據(jù)庫(kù)時(shí)就會(huì)拋錯(cuò)。

  首先解決MethodInvokingJobDetailFactoryBean的問(wèn)題,在不修改Spring源碼的情況下,可以避免使用這個(gè)類(lèi),直接調(diào)用JobDetail。但是使用JobDetail實(shí)現(xiàn),需要自己實(shí)現(xiàn)MothodInvoking的邏輯,可以使用JobDetail的jobClass和JobDataAsMap屬性來(lái)自定義一個(gè)Factory(Manager)來(lái)實(shí)現(xiàn)同樣的目的。例如,本示例中新建了一個(gè)MyDetailQuartzJobBean來(lái)實(shí)現(xiàn)這個(gè)功能。

3.2 MyDetailQuartzJobBean.java文件

package org.lxh.mvc.jobbean;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class MyDetailQuartzJobBean extends QuartzJobBean {
 protected final Log logger = LogFactory.getLog(getClass());
 private String targetObject;
 private String targetMethod;
 private ApplicationContext ctx;
 protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
  try {
   logger.info("execute [" + targetObject + "] at once>>>>>>");
   Object otargetObject = ctx.getBean(targetObject);
   Method m = null;
   try {
    m = otargetObject.getClass().getMethod(targetMethod, new Class[] {});
    m.invoke(otargetObject, new Object[] {});
   } catch (SecurityException e) {
    logger.error(e);
   } catch (NoSuchMethodException e) {
    logger.error(e);
   }
  } catch (Exception e) {
   throw new JobExecutionException(e);
  }
 }

 public void setApplicationContext(ApplicationContext applicationContext){
  this.ctx=applicationContext;
 }

 public void setTargetObject(String targetObject) {
  this.targetObject = targetObject;
 }

 public void setTargetMethod(String targetMethod) {
  this.targetMethod = targetMethod;
 }
}

3.3真正的Job實(shí)現(xiàn)類(lèi)

  在Test類(lèi)中,只是簡(jiǎn)單實(shí)現(xiàn)了打印系統(tǒng)當(dāng)前時(shí)間的功能。

package org.lxh.mvc.job;
import java.io.Serializable;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class Test implements Serializable{
 private Log logger = LogFactory.getLog(Test.class);
 private static final long serialVersionUID = -2073310586499744415L; 
 public void execute () {
  Date date=new Date(); 
  System.out.println(date.toLocaleString()); 
 }
}

3.4 配置quartz.xml文件

<bean id="Test" class="org.lxh.mvc.job.Test" scope="prototype">
 </bean>

 <bean id="TestjobTask" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass">
<value>org.lxh.mvc.jobbean.MyDetailQuartzJobBean</value>
  </property>
  <property name="jobDataAsMap">
   <map>
    <entry key="targetObject" value="Test" />
    <entry key="targetMethod" value="execute" />
    </map>
   </property> 
  </bean>
 
 <bean name="TestTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> 
  <property name="jobDetail" ref="TestjobTask" />
  <property name="cronExpression" value="0/1 * * * * ?" />
 </bean> 
<bean id="quartzScheduler"
 class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="configLocation" value="classpath:quartz.properties"/>
  <property name="triggers">
   <list>
    <ref bean="TestTrigger" />
   </list>
  </property>
  <property name="applicationContextSchedulerContextKey" value="applicationContext" />
 </bean>

3.5 測(cè)試

  ServerA、ServerB的代碼、配置完全一樣,先啟動(dòng)ServerA,后啟動(dòng)ServerB,當(dāng)Server關(guān)斷之后,ServerB會(huì)監(jiān)測(cè)到其關(guān)閉,并將ServerA上正在執(zhí)行的Job接管,繼續(xù)執(zhí)行。

4、Quartz集群實(shí)例(單獨(dú)Quartz)

  盡管我們已經(jīng)實(shí)現(xiàn)了Spring+Quartz的集群配置,但是因?yàn)镾pring與Quartz之間的兼容問(wèn)題還是不建議使用該方式。在本小節(jié)中,我們實(shí)現(xiàn)了單獨(dú)用Quartz配置的集群,相對(duì)Spring+Quartz的方式來(lái)說(shuō),簡(jiǎn)單、穩(wěn)定。

4.1 工程結(jié)構(gòu)

  我們采用單獨(dú)使用Quartz來(lái)實(shí)現(xiàn)其集群功能,代碼結(jié)構(gòu)及所需的第三方j(luò)ar包如圖3.1所示。其中,Mysql版本:5.1.52,Mysql驅(qū)動(dòng)版本:mysql-connector-java-5.1.5-bin.jar(針對(duì)于5.1.52,建議采用該版本驅(qū)動(dòng),因?yàn)镼uartz存在BUG使得其與某些Mysql驅(qū)動(dòng)結(jié)合時(shí)不能正常運(yùn)行)。

圖4.1 Quartz集群工程結(jié)構(gòu)及所需第三方j(luò)ar包

  其中quartz.properties為Quartz配置文件,放在src目錄下,若無(wú)該文件,Quartz將自動(dòng)加載jar包中的quartz.properties文件;SimpleRecoveryJob.java、SimpleRecoveryStatefulJob.java為兩個(gè)Job;ClusterExample.java中編寫(xiě)了調(diào)度信息、觸發(fā)機(jī)制及相應(yīng)的測(cè)試main函數(shù)。

4.2 配置文件quartz.properties

  默認(rèn)文件名稱(chēng)quartz.properties,通過(guò)設(shè)置"org.quartz.jobStore.isClustered"屬性為"true"來(lái)激活集群特性。在集群中的每一個(gè)實(shí)例都必須有一個(gè)唯一的"instance id" ("org.quartz.scheduler.instanceId" 屬性), 但是應(yīng)該有相同的"scheduler instance name" ("org.quartz.scheduler.instanceName"),也就是說(shuō)集群中的每一個(gè)實(shí)例都必須使用相同的quartz.properties 配置文件。除了以下幾種例外,配置文件的內(nèi)容其他都必須相同:

  a.線(xiàn)程池大小。

  b.不同的"org.quartz.scheduler.instanceId"屬性值(通過(guò)設(shè)定為"AUTO"即可)。

#============================================================== 
#Configure Main Scheduler Properties 
#============================================================== 
org.quartz.scheduler.instanceName = quartzScheduler
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 = 10000 
org.quartz.jobStore.dataSource = myDS
 
#============================================================== 
#Configure DataSource 
#============================================================== 
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://192.168.31.18:3306/test?useUnicode=true&amp;characterEncoding=UTF-8
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = 123456
org.quartz.dataSource.myDS.maxConnections = 30

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

4.3 ClusterExample.java文件

package cluster;
import java.util.Date;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
public class ClusterExample {
 
 public void cleanUp(Scheduler inScheduler) throws Exception {
  System.out.println("***** Deleting existing jobs/triggers *****");
  // unschedule jobs
  String[] groups = inScheduler.getTriggerGroupNames();
  for (int i = 0; i < groups.length; i++) {
   String[] names = inScheduler.getTriggerNames(groups[i]);
   for (int j = 0; j < names.length; j++) {
    inScheduler.unscheduleJob(names[j], groups[i]);
   }
  }
  // delete jobs
  groups = inScheduler.getJobGroupNames();
  for (int i = 0; i < groups.length; i++) {
   String[] names = inScheduler.getJobNames(groups[i]);
   for (int j = 0; j < names.length; j++) {
    inScheduler.deleteJob(names[j], groups[i]);
   }
  }
 }
  
 public void run(boolean inClearJobs, boolean inScheduleJobs) 
  throws Exception {
  // First we must get a reference to a scheduler
  SchedulerFactory sf = new StdSchedulerFactory();
  Scheduler sched = sf.getScheduler();
  
  if (inClearJobs) {
   cleanUp(sched);
  }
  System.out.println("------- Initialization Complete -----------");
  if (inScheduleJobs) {
   System.out.println("------- Scheduling Jobs ------------------");
   String schedId = sched.getSchedulerInstanceId();
   int count = 1;
   JobDetail job = new JobDetail("job_" + count, schedId, SimpleRecoveryJob.class);
   // ask scheduler to re-execute this job if it was in progress when
   // the scheduler went down...
   job.setRequestsRecovery(true);
   SimpleTrigger trigger = 
    new SimpleTrigger("triger_" + count, schedId, 200, 1000L);
   trigger.setStartTime(new Date(System.currentTimeMillis() + 1000L));
   System.out.println(job.getFullName() +
     " will run at: " + trigger.getNextFireTime() + 
     " and repeat: " + trigger.getRepeatCount() + 
     " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");
   sched.scheduleJob(job, trigger);
   count++;
   job = new JobDetail("job_" + count, schedId, 
     SimpleRecoveryStatefulJob.class);
   // ask scheduler to re-execute this job if it was in progress when
   // the scheduler went down...
   job.setRequestsRecovery(false);
   trigger = new SimpleTrigger("trig_" + count, schedId, 100, 2000L);
   trigger.setStartTime(new Date(System.currentTimeMillis() + 2000L));
   System.out.println(job.getFullName() +
     " will run at: " + trigger.getNextFireTime() + 
     " and repeat: " + trigger.getRepeatCount() + 
     " times, every " + trigger.getRepeatInterval() / 1000 + " seconds");
   sched.scheduleJob(job, trigger);
  }
  // jobs don't start firing until start() has been called...
  System.out.println("------- Starting Scheduler ---------------");
  sched.start();
  System.out.println("------- Started Scheduler ----------------");
  System.out.println("------- Waiting for one hour... ----------");
  try {
   Thread.sleep(3600L * 1000L);
  } catch (Exception e) {
  }
  System.out.println("------- Shutting Down --------------------");
  sched.shutdown();
  System.out.println("------- Shutdown Complete ----------------");
  }
 
 public static void main(String[] args) throws Exception {
  boolean clearJobs = true;
  boolean scheduleJobs = true;
  for (int i = 0; i < args.length; i++) {
   if (args[i].equalsIgnoreCase("clearJobs")) {
    clearJobs = true;    
   } else if (args[i].equalsIgnoreCase("dontScheduleJobs")) {
    scheduleJobs = false;
   }
  }
  ClusterExample example = new ClusterExample();
  example.run(clearJobs, scheduleJobs);
 }
}

4.4 SimpleRecoveryJob.java

package cluster;

import java.io.Serializable;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
//如果有想反復(fù)執(zhí)行的動(dòng)作,作業(yè),任務(wù)就把相關(guān)的代碼寫(xiě)在execute這個(gè)方法里,前提:實(shí)現(xiàn)Job這個(gè)接口
//至于SimpleJob這個(gè)類(lèi)什么時(shí)候?qū)嵗琫xecute這個(gè)方法何時(shí)被調(diào)用,我們不用關(guān)注,交給Quartz
public class SimpleRecoveryJob implements Job, Serializable {
 private static Log _log = LogFactory.getLog(SimpleRecoveryJob.class);
 public SimpleRecoveryJob() {
 }
 public void execute(JobExecutionContext context)
  throws JobExecutionException {
  //這個(gè)作業(yè)只是簡(jiǎn)單的打印出作業(yè)名字和此作業(yè)運(yùn)行的時(shí)間
  String jobName = context.getJobDetail().getFullName();
  System.out.println("JOB 1111111111111111111 SimpleRecoveryJob says: " + jobName + " executing at " + new Date());
 }
}

4.5 運(yùn)行結(jié)果

  Server A與Server B中的配置和代碼完全一樣。運(yùn)行方法:運(yùn)行任意主機(jī)上的ClusterExample.java,將任務(wù)加入調(diào)度,觀察運(yùn)行結(jié)果:

  運(yùn)行ServerA,結(jié)果如圖4.2所示。

圖4.2 ServerA運(yùn)行結(jié)果1  

  開(kāi)啟ServerB后,ServerA與ServerB的輸出如圖4.3、4.4所示。

圖4.3 ServerA運(yùn)行結(jié)果2

圖4.4 ServerB運(yùn)行結(jié)果1

  從圖4.3、4.4可以看出,ServerB開(kāi)啟后,系統(tǒng)自動(dòng)實(shí)現(xiàn)了負(fù)責(zé)均衡,ServerB接手Job1。關(guān)斷ServerA后,ServerB的運(yùn)行結(jié)果如圖4.5所示。

圖4.5 ServerB運(yùn)行結(jié)果2

  從圖4.5中可以看出,ServerB可以檢測(cè)出ServerA丟失,將其負(fù)責(zé)的任務(wù)Job2接手,并將ServerA丟失到Server檢測(cè)出這段異常時(shí)間中需要執(zhí)行的Job2重新執(zhí)行了。

5、注意事項(xiàng)

5.1 時(shí)間同步問(wèn)題

  Quartz實(shí)際并不關(guān)心你是在相同還是不同的機(jī)器上運(yùn)行節(jié)點(diǎn)。當(dāng)集群放置在不同的機(jī)器上時(shí),稱(chēng)之為水平集群。節(jié)點(diǎn)跑在同一臺(tái)機(jī)器上時(shí),稱(chēng)之為垂直集群。對(duì)于垂直集群,存在著單點(diǎn)故障的問(wèn)題。這對(duì)高可用性的應(yīng)用來(lái)說(shuō)是無(wú)法接受的,因?yàn)橐坏C(jī)器崩潰了,所有的節(jié)點(diǎn)也就被終止了。對(duì)于水平集群,存在著時(shí)間同步問(wèn)題。

  節(jié)點(diǎn)用時(shí)間戳來(lái)通知其他實(shí)例它自己的最后檢入時(shí)間。假如節(jié)點(diǎn)的時(shí)鐘被設(shè)置為將來(lái)的時(shí)間,那么運(yùn)行中的Scheduler將再也意識(shí)不到那個(gè)結(jié)點(diǎn)已經(jīng)宕掉了。另一方面,如果某個(gè)節(jié)點(diǎn)的時(shí)鐘被設(shè)置為過(guò)去的時(shí)間,也許另一節(jié)點(diǎn)就會(huì)認(rèn)定那個(gè)節(jié)點(diǎn)已宕掉并試圖接過(guò)它的Job重運(yùn)行。最簡(jiǎn)單的同步計(jì)算機(jī)時(shí)鐘的方式是使用某一個(gè)Internet時(shí)間服務(wù)器(Internet Time Server ITS)。

5.2 節(jié)點(diǎn)爭(zhēng)搶Job問(wèn)題

  因?yàn)镼uartz使用了一個(gè)隨機(jī)的負(fù)載均衡算法, Job以隨機(jī)的方式由不同的實(shí)例執(zhí)行。Quartz官網(wǎng)上提到當(dāng)前,還不存在一個(gè)方法來(lái)指派(釘住) 一個(gè) Job 到集群中特定的節(jié)點(diǎn)。

5.3 從集群獲取Job列表問(wèn)題

  當(dāng)前,如果不直接進(jìn)到數(shù)據(jù)庫(kù)查詢(xún)的話(huà),還沒(méi)有一個(gè)簡(jiǎn)單的方式來(lái)得到集群中所有正在執(zhí)行的Job列表。請(qǐng)求一個(gè)Scheduler實(shí)例,將只能得到在那個(gè)實(shí)例上正運(yùn)行Job的列表。Quartz官網(wǎng)建議可以通過(guò)寫(xiě)一些訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)JDBC代碼來(lái)從相應(yīng)的表中獲取全部的Job信息。

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • java實(shí)現(xiàn)學(xué)生管理系統(tǒng)(面向?qū)ο?

    java實(shí)現(xiàn)學(xué)生管理系統(tǒng)(面向?qū)ο?

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)學(xué)生管理系統(tǒng)(面向?qū)ο螅?,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • spring boot整合quartz實(shí)現(xiàn)多個(gè)定時(shí)任務(wù)的方法

    spring boot整合quartz實(shí)現(xiàn)多個(gè)定時(shí)任務(wù)的方法

    這篇文章主要介紹了spring boot整合quartz實(shí)現(xiàn)多個(gè)定時(shí)任務(wù)的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Java編程ssh整合常見(jiàn)錯(cuò)誤解析

    Java編程ssh整合常見(jiàn)錯(cuò)誤解析

    這篇文章主要介紹了Java編程ssh整合常見(jiàn)錯(cuò)誤解析,如果有什么不足之處,歡迎留言補(bǔ)充。
    2017-10-10
  • SpringBoot-RestTemplate如何實(shí)現(xiàn)調(diào)用第三方API

    SpringBoot-RestTemplate如何實(shí)現(xiàn)調(diào)用第三方API

    這篇文章主要介紹了SpringBoot-RestTemplate實(shí)現(xiàn)調(diào)用第三方API的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 通過(guò)實(shí)例解析spring bean之間的關(guān)系

    通過(guò)實(shí)例解析spring bean之間的關(guān)系

    這篇文章主要介紹了通過(guò)實(shí)例解析spring bean之間的關(guān)系,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • java關(guān)鍵字final使用方法詳解

    java關(guān)鍵字final使用方法詳解

    在程序設(shè)計(jì)中,我們有時(shí)可能希望某些數(shù)據(jù)是不能夠改變的,這個(gè)時(shí)候final就有用武之地了。final是java的關(guān)鍵字,本文就詳細(xì)說(shuō)明一下他的使用方法
    2013-11-11
  • Java多線(xiàn)程的實(shí)現(xiàn)方式詳解

    Java多線(xiàn)程的實(shí)現(xiàn)方式詳解

    這篇文章主要介紹了Java多線(xiàn)程的實(shí)現(xiàn)方式詳解,線(xiàn)程就是進(jìn)程中的單個(gè)順序控制流,也可以理解成是一條執(zhí)行路徑,java中之所以有多線(xiàn)程機(jī)制,目的就是為了提高程序的處理效率,需要的朋友可以參考下
    2023-08-08
  • Java中Stream?API的使用示例詳解

    Java中Stream?API的使用示例詳解

    Java?在?Java?8?中提供了一個(gè)新的附加包,稱(chēng)為?java.util.stream,該包由類(lèi)、接口和枚舉組成,允許對(duì)元素進(jìn)行函數(shù)式操作,?本文主要介紹了Java中Stream?API的具體使用,感興趣的小伙伴可以了解下
    2023-11-11
  • mybatis整合ehcache做三級(jí)緩存的實(shí)現(xiàn)方法

    mybatis整合ehcache做三級(jí)緩存的實(shí)現(xiàn)方法

    ehcache是一個(gè)快速內(nèi)存緩存框架,java項(xiàng)目里用起來(lái)很方便,下面這篇文章主要給大家介紹了關(guān)于mybatis整合ehcache做三級(jí)緩存的實(shí)現(xiàn)方法,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • Java利用EasyExcel實(shí)現(xiàn)合并單元格

    Java利用EasyExcel實(shí)現(xiàn)合并單元格

    在某些業(yè)務(wù)場(chǎng)景中可能會(huì)有合并單元格的需求,本文將詳細(xì)為大家講解Java如何利用EasyExcel實(shí)現(xiàn)合并單元格,感興趣的小伙伴可以了解一下
    2022-06-06

最新評(píng)論