首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring + Quartz 打造高可用分散式定時任務框架實戰

     編輯:Java 相關知識

業務背景與痛點

隨著業務增長,單機部署無法滿足高併發需求,遷移至叢集後,原有定時任務在每台節點重複執行,導致資料重複處理、資源浪費。早期採用「人工分片」偽分散式方案,節點故障時任務中斷,嚴重影響業務連續性。

Quartz 原生叢集原理

Quartz 透過資料庫鎖機制(QRTZ_LOCKS 表)確保同一時刻只有一個節點執行特定任務。核心配置:

  • org.quartz.jobStore.isClustered = true
  • org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
  • org.quartz.scheduler.instanceId = AUTO(自動生成節點 ID)

完整整合配置

1. Spring XML 配置

<beans ...>
  <context:component-scan base-package="com.example.job"/>
  
  <!-- 資料源 (JNDI) -->
  <bean name="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/quartz"/>
  </bean>
  
  <!-- 事務管理 -->
  <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>
  
  <!-- 執行緒池 -->
  <bean name="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="15"/>
    <property name="maxPoolSize" value="25"/>
    <property name="queueCapacity" value="100"/>
  </bean>
  
  <!-- SchedulerFactoryBean -->
  <bean name="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="configLocation" value="classpath:quartz.properties"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="transactionManager" ref="transactionManager"/>
    <property name="schedulerName" value="baseScheduler"/>
    <property name="overwriteExistingJobs" value="true"/>
    <property name="applicationContextSchedulerContextKey" value="applicationContext"/>
    <property name="jobFactory">
      <bean class="com.example.autowired.AutowiringSpringBeanJobFactory"/>
    </property>
    <property name="triggers">
      <list><ref bean="sampleTrigger"/></list>
    </property>
    <property name="jobDetails">
      <list><ref bean="sampleJobDetail"/></list>
    </property>
    <property name="taskExecutor" ref="taskExecutor"/>
  </bean>
  
  <!-- JobDetail -->
  <bean name="sampleJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.example.job.SampleJob"/>
    <property name="durability" value="true"/>
    <property name="requestsRecovery" value="false"/>
  </bean>
  
  <!-- CronTrigger -->
  <bean name="sampleTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="sampleJobDetail"/>
    <property name="cronExpression" value="0/10 * * * * ?"/>
    <property name="timeZone" value="GMT+8:00"/>
  </bean>
</beans>

2. quartz.properties 關鍵配置

# JobStore 叢集配置
org.quartz.jobStore.useProperties=true
org.quartz.jobStore.tablePrefix=QRTZ_
org.quartz.jobStore.isClustered=true
org.quartz.jobStore.clusterCheckinInterval=5000
org.quartz.jobStore.misfireThreshold=60000
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate

# Scheduler
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.instanceName=MY_CLUSTERED_SCHEDULER

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

3. 支援 Spring 注入的 JobFactory

public class AutowiringSpringBeanJobFactory 
    extends SpringBeanJobFactory implements ApplicationContextAware {
    
    private AutowireCapableBeanFactory beanFactory;
    
    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        beanFactory = ctx.getAutowireCapableBeanFactory();
    }
    
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        Object job = super.createJobInstance(bundle);
        beanFactory.autowireBean(job);
        return job;
    }
}

4. 業務 Job 實現

@Component
public class SampleJob extends QuartzJobBean {
    private static final Log LOG = LogFactory.getLog(SampleJob.class);
    
    @Autowired
    private BusinessService businessService;
    
    @Override
    protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
        LOG.info("任務開始: " + new Date());
        businessService.doWork();
        LOG.info("任務結束: " + new Date());
    }
}

叢集驗證測試

  1. 啟動節點 A (port 8080) → 任務每 10 秒執行一次
  2. 啟動節點 B (port 8888) → 僅節點 A 執行,節點 B 待機
  3. 關閉節點 A → 節點 B 自動接管,無縫切換

驗證結果證明:同一排程任務在叢集中任意時刻僅由單一節點執行,具備故障轉移能力。

生產環境優化建議

  • 資料庫連接池調優(HikariCP)
  • 監控 QRTZ_FIRED_TRIGGERS 表檢測任務積壓
  • 關鍵任務啟用 requestsRecovery=true 確保重啟後補償
  • 結合 XXL-Job 或 ElasticJob 實現更豐富的分片、監控、報警功能
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved