Maison  >  Article  >  Java  >  Implémentation d'un cadre de tâches planifiées distribuées basé sur Spring Quartz

Implémentation d'un cadre de tâches planifiées distribuées basé sur Spring Quartz

高洛峰
高洛峰original
2017-02-07 15:24:591354parcourir

Contexte du problème

Notre société est une startup à croissance rapide comptant actuellement 200 personnes. Son activité principale est liée au tourisme et à l'hôtellerie. Le cycle d'itération et de mise à jour des applications est relativement rapide, les développeurs passent donc plus de temps. mettre à jour = suivre le rythme des itérations, mais manquant de contrôle sur l'ensemble du système

Avant l'existence d'un cluster, les tâches planifiées de l'entreprise étaient mises en œuvre

Au début, le nombre de les visites sur l'application n'étaient pas si importantes qu'un seul serveur peut pleinement satisfaire l'utilisation. Il existe de nombreuses tâches planifiées qui doivent être exécutées dans l'application

Avec le cluster, les tâches planifiées de l'entreprise sont en quelque sorte mises en œuvre

À mesure que le nombre d'utilisateurs augmente, le nombre de visites augmente également. Par conséquent, un serveur ne peut pas répondre aux exigences élevées de concurrence, donc l'entreprise déploie l'application sur le cluster, et le front-end se fait via le proxy nginx. (l'IP du serveur d'applications peut être isolée par un pare-feu pour éviter un accès direct via le nom d'application du port IP).

Dans un environnement de cluster, la même tâche planifiée sera exécutée sur chaque machine du cluster. De cette manière, la tâche planifiée sera exécutée à plusieurs reprises, ce qui augmentera non seulement la charge sur le serveur, mais également. entraîner une surcharge supplémentaire en raison de l'exécution répétée de la tâche planifiée. Des erreurs imprévisibles. La solution de l'entreprise consiste donc à répartir uniformément les tâches dans les tâches planifiées sur chaque machine du cluster en fonction du nombre de clusters (le score moyen ici se réfère au précédent). heure à laquelle une tâche planifiée était initialement sur une machine). Exécutez sur chaque machine, divisez d'abord artificiellement la tâche en plusieurs parties et laissez toutes les machines exécuter cette personne)

Défauts dans la manière actuelle d'implémenter les tâches planifiées dans le cluster

Actuellement l'entreprise La manière de traiter les tâches planifiées dans le cluster n'est pas une véritable méthode de traitement distribuée, mais une méthode pseudo-distribuée (communément appelée méthode native au sein de l'entreprise). La méthode est que lorsque la machine du cluster tombe en panne, toutes les tâches planifiées se bloquent ou ne peuvent pas être exécutées en même temps, ce qui aura un impact sérieux sur l'entreprise

Solutions aux défauts (l'objet de cet article )

Utilisez Spring Quartz pour créer un ensemble de véritables systèmes de tâches planifiées distribuées. Après avoir consulté les informations pertinentes, nous avons appris que le framework Quartz prend en charge nativement les tâches planifiées distribuées

IDE de développement : Intellij IDEA

Utilisez Spring Quartz pour créer un ensemble de tâches planifiées distribuées. 🎜>

Version JDK : 1.8

Version Spring : 4.2.6

Version Quartz : 2.2.1

Configuration de l'intégration Spring et Quartz
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
 
  <context:component-scan base-package="com.aaron.clusterquartz.job"/>
 
  <bean name="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <!-- tomcat -->
    <!--<property name="jndiName" value="java:comp/env/jndi/mysql/quartz"/>-->
 
    <!-- jboss -->
    <property name="jndiName" value="jdbc/quartz"/>
  </bean>
  <!-- 分布式事务配置 start -->
 
  <!-- 配置线程池-->
  <bean name="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="15"/>
    <property name="maxPoolSize" value="25"/>
    <property name="queueCapacity" value="100"/>
  </bean>
 
  <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>
 
  <!-- 配置调度任务-->
  <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="appli"/>
 
    <property name="jobFactory">
      <bean class="com.aaron.clusterquartz.autowired.AutowiringSpringBeanJobFactory"/>
    </property>
 
    <property name="triggers">
      <list>
        <ref bean="printCurrentTimeScheduler"/>
      </list>
    </property>
    <property name="jobDetails">
      <list>
        <ref bean="printCurrentTimeJobs"/>
      </list>
    </property>
 
    <property name="taskExecutor" ref="executor"/>
 
  </bean>
 
  <!-- 配置Job详情 -->
  <bean name="printCurrentTimeJobs" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="com.aaron.clusterquartz.job.PrintCurrentTimeJobs"/>
    <!--因为我使用了spring的注解,所以这里可以不用配置scheduler的属性-->
    <!--<property name="jobDataAsMap">
      <map>
        <entry key="clusterQuartz" value="com.aaron.framework.clusterquartz.job.ClusterQuartz"/>
      </map>
    </property>-->
    <property name="durability" value="true"/>
    <property name="requestsRecovery" value="false"/>
  </bean>
 
  <!-- 配置触发时间 -->
  <bean name="printCurrentTimeScheduler" class="com.aaron.clusterquartz.cron.PersistableCronTriggerFactoryBean">
    <property name="jobDetail" ref="printCurrentTimeJobs"/>
    <property name="cronExpression">
      <value>0/10 * * * * ?</value>
    </property>
    <property name="timeZone">
      <value>GMT+8:00</value>
    </property>
  </bean>
 
  <!-- 分布式事务配置 end -->
</beans>

Fichier de propriétés quartz
#============================================================================
# Configure JobStore
# Using Spring datasource in quartzJobsConfig.xml
# Spring uses LocalDataSourceJobStore extension of JobStoreCMT
#============================================================================
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.txIsolationLevelReadCommitted = true
 
# Change this to match your DB vendor
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
 
 
#============================================================================
# Configure Main Scheduler Properties
# Needed to manage cluster instances
#============================================================================
org.quartz.scheduler.instanceId=AUTO
org.quartz.scheduler.instanceName=MY_CLUSTERED_JOB_SCHEDULER
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
 
 
#============================================================================
# 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


Description de la classe associée


La classe AutowiringSpringBeanJobFactory doit utiliser les annotations Spring dans le planificateur. Si vous n'utilisez pas d'annotations, vous ne pouvez pas l'appliquer. class et utilisez

SpringBeanJobFactory
package com.aaron.clusterquartz.autowired;
 
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
 
/**
 * @author 
 * @description 使job类支持spring的自动注入
 * @date 2016-05-27
 */
public class AutowiringSpringBeanJobFactory extends SpringBeanJobFactory implements ApplicationContextAware
{
  private transient AutowireCapableBeanFactory beanFactory;
 
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
  {
    beanFactory = applicationContext.getAutowireCapableBeanFactory();
  }
 
 
  @Override
  protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception
  {
    Object job = super.createJobInstance(bundle);
    beanFactory.autowireBean(job);
    return job;
  }
}
package com.aaron.clusterquartz.job;
 
import com.arron.util.DateUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
import java.util.Date;
 
/**
 * @author 
 * @description 一句话描述该文件的用途
 * @date 2016-05-23
 */
public class PrintCurrentTimeJobs extends QuartzJobBean
{
  private static final Log LOG_RECORD = LogFactory.getLog(PrintCurrentTimeJobs.class);
 
  //这里就是因为有上文中的AutowiringSpringBeanJobFactory才可以使用@Autowired注解,否则只能在配置文件中设置这属性的值
  @Autowired
  private ClusterQuartz clusterQuartz;
 
 
  protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException
  {
    LOG_RECORD.info("begin to execute task," + DateUtils.dateToString(new Date()));
 
    clusterQuartz.printUserInfo();
 
    LOG_RECORD.info("end to execute task," + DateUtils.dateToString(new Date()));
 
  }
}

Résultats des tests :

Comme il n'y a qu'un seul ordinateur, j'ai ouvert deux ports 8080 et 8888 pour tester, j'ai défini le. la tâche planifiée ci-dessus doit être exécutée toutes les 10 secondes.

Quand seulement je démarre le port 8080, vous pouvez voir que la console imprime une déclaration toutes les 10 secondes

基于spring quartz的分布式定时任务框架实现

Dans un test de comparaison où deux ports sont démarrés à en même temps On peut voir qu'un seul port exécute la tâche planifiée

基于spring quartz的分布式定时任务框架实现

Après avoir fermé le port qui exécute la tâche planifiée, l'autre port qui n'était pas en cours d'exécution avant commence à prendre le relais et continue de s'exécuter

基于spring quartz的分布式定时任务框架实现

À ce stade, nous pouvons clairement voir que dans une tâche planifiée distribuée (ou cluster), une seule tâche planifiée sera. courir en même temps.

Ce qui précède représente l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'apprentissage de chacun. J'espère également que tout le monde soutiendra le site Web PHP chinois.

Pour plus d'articles liés à la mise en œuvre d'un cadre de tâches de timing distribué basé sur Spring Quartz, veuillez faire attention au site Web PHP chinois !

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn