1 基本信息
2 Quartz的集羣配置
Quartz是一個開源的作業調度框架,它完全由java寫成,並設計用於J2SE和J2EE應用中。它提供了巨大的靈活性而不犧牲簡單性。你能夠用它來爲執行一個作業而創建簡單的或複雜的調度。它有很多特徵,如:數據庫支持,集羣,插件,EJB作業預構建,JavaMail及其它,支持cron-like表達式等等。其中集羣配置一般比較複雜,那麼在Quartz中如何配置它的集羣特性呢?
2.1 實現集羣的基本原理
目前Quartz最新版本是1.6.0。Quartz是通過藉助關係數據庫和JDBC作業存儲來實現集羣管理的。
1. 原理
集羣通過故障切換和負載平衡的功能,能給調度器帶來高可用性和伸縮性。目前集羣只能工作在JDBC-Jobstore (JobStoreTX 或者JobStoreCMT)方式下,從本質上來說,是使集羣上的每一個節點通過共享同一個數據庫來工作的(Quartz通過啓動兩個維護線程來維護數據庫狀態實現集羣管理,一個是檢測節點狀態線程,一個是恢復任務線程)。
負載平衡是自動完成的,集羣的每個節點會盡快觸發任務。當一個觸發器的觸發時間到達時,第一個節點將會獲得任務(通過鎖定),成爲執行任務的節點。
故障切換的發生是在當一個節點正在執行一個或者多個任務失敗的時候。當一個節點失敗了,其他的節點會檢測到並且標識在失敗節點上正在進行的數據庫中的任務。任何被標記爲可恢復(任務詳細信息的"requests recovery"屬性)的任務都會被其他的節點重新執行。沒有標記可恢復的任務只會被釋放出來,將會在下次相關觸發器觸發時執行。
2. 集羣管理用到的表
--任務詳細信息表
- CREATE TABLE qrtz_job_details
- (
- JOB_NAME VARCHAR2(80) NOT NULL,
- JOB_GROUP VARCHAR2(80) NOT NULL,
- DESCRIPTION VARCHAR2(120) NULL,
- JOB_CLASS_NAME VARCHAR2(128) NOT NULL,
- IS_DURABLE VARCHAR2(1) NOT NULL,
- IS_VOLATILE VARCHAR2(1) NOT NULL,
- IS_STATEFUL VARCHAR2(1) NOT NULL,
- REQUESTS_RECOVERY VARCHAR2(1) NOT NULL, --可恢復標記
- JOB_DATA BLOB NULL,
- PRIMARY KEY (JOB_NAME,JOB_GROUP)
- );
--觸發器與任務關聯表
- CREATE TABLE qrtz_fired_triggers
- (
- ENTRY_ID VARCHAR2(95) NOT NULL,
- TRIGGER_NAME VARCHAR2(80) NOT NULL,
- TRIGGER_GROUP VARCHAR2(80) NOT NULL,
- IS_VOLATILE VARCHAR2(1) NOT NULL,
- INSTANCE_NAME VARCHAR2(80) NOT NULL,
- FIRED_TIME NUMBER(13) NOT NULL,
- STATE VARCHAR2(16) NOT NULL,
- JOB_NAME VARCHAR2(80) NULL,
- JOB_GROUP VARCHAR2(80) NULL,
- IS_STATEFUL VARCHAR2(1) NULL,
- REQUESTS_RECOVERY VARCHAR2(1) NULL, --可恢復標記
- PRIMARY KEY (ENTRY_ID)
- );
--調度器狀態表
- TABLE qrtz_scheduler_state
- (
- INSTANCE_NAME VARCHAR2(80) NOT NULL, --調度器實例ID
- LAST_CHECKIN_TIME NUMBER(13) NOT NULL, --上次檢查時間
- CHECKIN_INTERVAL NUMBER(13) NOT NULL, --檢查時間間隔
- RECOVERER VARCHAR2(80) NULL, --恢復調度器
- PRIMARY KEY (INSTANCE_NAME)
- );
2.2 集羣配置
通過設置"org.quartz.jobStore.isClustered"屬性爲"true"來激活集羣特性。在集羣中的每一個實例都必須有一個唯一的"instance id" ("org.quartz.scheduler.instanceId" 屬性), 但是應該有相同的"scheduler instance name" ("org.quartz.scheduler.instanceName"),也就是說集羣中的每一個實例都必須使用相同的quartz.properties 配置文件。除了以下幾種例外,配置文件的內容其他都必須相同:
不同的線程池大小,
不同的"org.quartz.scheduler.instanceId"屬性值(這個可以很容易做到,設定爲"AUTO"即可)。
注意: 永遠不要在不同的機器上運行集羣,除非他們的時鐘是使用某種形式的同步服務(守護)非常有規律的運行(時鐘必須在一分一秒內)來達到同步。還有: 永遠不要觸發一個非集羣的實例,如果其他的實例正在同一個數據庫表上運行。你將使你的數據嚴重腐蝕,出現非預期行爲。
示例及詳細配置說明,請參照附錄Quartz配置文件說明。
3 附錄
3.1 Quartz配置文件說明
3.1.1 Quartz配置文件基本說明
文件名稱:默認文件名稱quartz.properties,可以通過更改系統屬性“org.quartz.properties”來加載自定義的配置。
格式:屬性文件
3.1.2 Quartz配置文件詳細說明
3.1.2.1 Scheduler主要屬性的配置
- # Scheduler主要屬性的一般定義模式如下:
- #
- # org.quartz.scheduler.instanceName = SCHED_NAME
- # org.quartz.scheduler.instanceId = INSTANCE_ID
- # org.quartz.scheduler.threadName = THREAD_NAME
- # org.quartz.scheduler.rmi.export = false
- # org.quartz.scheduler.rmi.proxy = false
- # org.quartz.scheduler.rmi.registryHost = localhost
- # org.quartz.scheduler.rmi.registryPort = 1099
- # org.quartz.scheduler.rmi.createRegistry = never
- # org.quartz.scheduler.userTransactionURL = USER_TX_LOCATION
- # org.quartz.scheduler.wrapJobExecutionInUserTransaction = JOBS_IN_USER_TX
- # org.quartz.scheduler.idleWaitTime = IDLE_WAIT_TIME
- # org.quartz.scheduler.dbFailureRetryInterval = DB_FAILURE_RETRY_INTERVAL
- # org.quartz.scheduler.classLoadHelper.class = CLASS_LOAD_HELPER_CLASS
- # org.quartz.context.key.SOME_KEY = SOME_VALUE
下面是具體說明:
3.1.2.2 線程池(ThreadPool)的配置
下面是具體說明:
- # 定製一個線程池的一般模式如下:
- #
- # org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
- # org.quartz.threadPool.threadCount = THREAD_COUNT
- # org.quartz.threadPool.threadPriority = THREAD_PRIO
- #
- # 簡單線程池(SimpleThreadPool)的選項參數:
- #
- # org.quartz.threadPool.makeThreadsDaemons = DAEMON_THREADS
- # org.quartz.threadPool.threadsInheritGroupOfInitializingThread = INHERIT_GRP
- # org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = INHERIT_LDR
- #
- # or
- #
- # org.quartz.threadPool.class = com.mycompany.goo.FooThreadPool
- # org.quartz.threadPool.somePropOfFooThreadPool = someValue
- #
3.1.2.3 任務存儲(JobStore)的配置
- #
- # 定義一個任務存儲的一般模式如下:
- #
- # org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
- # org.quartz.jobStore.misfireThreshold = MISFIRE_THRESHOLD
- #
- # or
- #
- # org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.<JobStoreClass>
- # JobStoreClass 是下面其中的一個:
- # - JobStoreTX 用於單機(standalone-Quartz)實現
- # - JobStoreCMT 用於基於應用服務器容器管理事務(appserver-based container-managed transaction )的Quartz 實現
- #
- # org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.<DriverDelegateClass>
- # DriverDelegateClass 是下面其中的一個:
- # - StdJDBCDelegate (用於許多 JDBC-compliant drivers)
- # - MSSQLDelegate (用於 Microsoft SQL Server drivers)
- # - PostgreSQLDelegate (用於 PostgreSQL drivers)
- # - WebLogicDelegate (用於 WebLogic drivers)
- # - oracle.OracleDelegate (用於 Oracle drivers)
- #
- # org.quartz.jobStore.useProperties = USE_PROPERTIES
- # org.quartz.jobStore.dataSource = DS_NAME
- # org.quartz.jobStore.tablePrefix = TABLE_PREFIX
- # org.quartz.jobStore.isClustered = IS_CLUSTERED
- # org.quartz.jobStore.selectWithLockSQL = LOCKING_SELECT_STATEMENT
- # org.quartz.jobStore.dontSetAutoCommitFalse = DONT_TURN_OFF_AUTO_COMMIT
- # org.quartz.jobStore.maxMisfiresToHandleAtATime = MAX_MISFIRE_HANDLE
- # org.quartz.jobStore.txIsolationLevelSerializable = SERIALIZABLE_ISOLATION
- #
- # 如果你使用JobStoreCMT,你還需要下面的參數:
- #
- # org.quartz.jobStore.nonManagedTXDataSource = NON_MANAGED_TX_DS_NAME
- #
- # 並且如果你使用JobStoreCMT,下面的參數是可選的:
- #
- # org.quartz.jobStore.dontSetNonManagedTXConnectionAutoCommitFalse = DONT_TURN_OFF_AUTO_COMMIT
- # org.quartz.jobStore.txIsolationLevelReadCommitted = READ_COMMITTED_ISOLATION
- #
- #
- # 或者,使用一個用戶自定義JobStore實現:
- #
- # org.quartz.jobStore.class = com.mycompany.goo.FooJobStore
- # org.quartz.jobStore.somePropOfFooJobStore = someValue
- #
- #
下面是具體說明:
3.1.2.4 數據源的配置
- # (只有當使用JDBCJobStore時需要, 或者一個插件需要JDBC)
- # -- 如果你的Scheduler非常忙碌,比如在一定的線程池內執行相同數目的任務,那麼你應讓數據源的連接數等於線程數 + 1
- #
- # 數據源定義的一般模式如下:
- #
- # org.quartz.dataSource.NAME.driver = DRIVER_CLASS_NAME
- # org.quartz.dataSource.NAME.URL = DB_URL
- # org.quartz.dataSource.NAME.user = DB_USER
- # org.quartz.dataSource.NAME.password = DB_PASSWORD
- # org.quartz.dataSource.NAME.maxConnections = DB_POOL_SIZE
- # org.quartz.dataSource.NAME.validationQuery= VALIDATION_QUERY
- #
- # or
- #
- # org.quartz.dataSource.NAME.jndiURL = DB_JNDI_URL
- #
- # or
- # org.quartz.dataSource.NAME.jndiURL = DB_JNDI_URL
- # org.quartz.dataSource.NAME.jndiAlwaysLookup = DB_JNDI_ALWAYS_LOOKUP
- # org.quartz.dataSource.NAME.java.naming.factory.initial = JNDI_CTXT_FACTORY
- # org.quartz.dataSource.NAME.java.naming.provider.url = JNDI_PROVIDER_URL
- # org.quartz.dataSource.NAME.java.naming.security.principal = JNDI_PRINCIPAL
- # org.quartz.dataSource.NAME.java.naming.security.credentials = JNDI_CREDENTIALS
- #
- #
上面顯示了兩種數據源定義方式:一個數據源可以用給定的數據庫連接信息創建,也可以是利用應用服務器管理生成的JNDI數據源的邏輯映射。
下面是具體說明:
3.1.2.5 Scheduler插件的配置
- # SchedulerPlugin定義的一般模式如下:
- #
- # org.quartz.plugin.NAME.class = PLUGIN_CLASS_NAME
- #
- # 如果這個插件類有一些屬性值需要通過"setter"方法設定, 名稱和值的屬性定義如下:
- #
- # org.quartz.plugin.NAME.propName = propValue
- #
- # ..."propName" 在插件類中會有一個"setPropName"方法.但是隻支持原始數據類型(包括 Strings)。
- #
配置插件的簡單示例:
- org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin
- org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}
- org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}
- org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.JobInitializationPlugin
- org.quartz.plugin.jobInitializer.fileName = data/my_job_data.xml
- org.quartz.plugin.jobInitializer.overWriteExistingJobs = false
- org.quartz.plugin.jobInitializer.failOnFileNotFound = true
- org.quartz.plugin.shutdownhook.class = org.quartz.plugins.management.ShutdownHookPlugin
- org.quartz.plugin.shutdownhook.cleanShutdown = true
3.1.3 示例
- #============================================================
- # Configure Main Scheduler Properties
- #===========================================================
- org.quartz.scheduler.instanceName = MyClusteredScheduler
- org.quartz.scheduler.instanceId = AUTO
- #===========================================================
- # Configure ThreadPool
- #===========================================================
- org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
- org.quartz.threadPool.threadCount = 25
- org.quartz.threadPool.threadPriority = 5
- #===========================================================
- # Configure JobStore
- #===========================================================
- org.quartz.jobStore.misfireThreshold = 60000
- org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.oracle.OracleDelegate
- org.quartz.jobStore.useProperties = false
- org.quartz.jobStore.dataSource = myDS
- org.quartz.jobStore.tablePrefix = QRTZ_
- org.quartz.jobStore.isClustered = true
- org.quartz.jobStore.clusterCheckinInterval = 20000
- #===========================================================
- # Configure Datasources
- #===========================================================
- org.quartz.dataSource.myDS.driver = oracle.jdbc.driver.OracleDriver
- org.quartz.dataSource.myDS.URL = jdbc:oracle:thin:@cluster:1521:dev
- org.quartz.dataSource.myDS.user = quartz
- org.quartz.dataSource.myDS.password = quartz
- org.quartz.dataSource.myDS.maxConnections = 5
- org.quartz.dataSource.myDS.validationQuery=select 0 from dual