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.8Version Spring : 4.2.6Version Quartz : 2.2.1Configuration 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
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
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
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
À 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 !