Heim >Java >javaLernprogramm >Implementierung eines Frameworks für verteilte geplante Aufgaben basierend auf Spring+Quartz
Problemhintergrund
Unser Unternehmen ist ein schnell wachsendes Startup-Unternehmen mit derzeit 200 Mitarbeitern. Der Anwendungsiterations- und Aktualisierungszyklus ist relativ schnell, sodass Entwickler mehr Zeit aufwenden aktualisieren = mit dem Tempo der Iterationen Schritt halten, aber keine Kontrolle über das gesamte System
Bevor es einen Cluster gab, wurden die geplanten Aufgaben des Unternehmens implementiert
In den frühen Tagen war die Anzahl der Die Besuche der Anwendung waren nicht so groß, dass ein Server die Nutzung vollständig erfüllen kann. Es gibt viele geplante Aufgaben, die in der Anwendung ausgeführt werden müssen
Mit dem Cluster werden die geplanten Aufgaben des Unternehmens in gewisser Weise implementiert
Mit zunehmender Benutzerzahl steigt auch die Anzahl der Besuche. Infolgedessen kann ein Server die hohen Parallelitätsanforderungen nicht erfüllen, sodass das Unternehmen die Anwendung im Cluster bereitstellt und das Front-End über den Nginx-Proxy erfolgt (Die IP des Anwendungsservers kann durch eine Firewall isoliert werden, wodurch die direkte Verwendung der Zugriffsmethode IP + Port + Anwendungsname vermieden wird.)
In einer Clusterumgebung wird die gleiche geplante Aufgabe auf jedem Computer im Cluster ausgeführt. Auf diese Weise wird die geplante Aufgabe wiederholt ausgeführt, was nicht nur die Belastung des Servers erhöht, sondern auch Aufgrund der wiederholten Ausführung geplanter Aufgaben verursachen unvorhersehbare Fehler zusätzlichen Aufwand. Daher besteht die Lösung des Unternehmens darin, die Aufgaben in den geplanten Aufgaben entsprechend der Anzahl der Cluster gleichmäßig auf jede Maschine zu verteilen (die durchschnittliche Bewertung bezieht sich hier auf die vorherige). Zeit, in der sich eine geplante Aufgabe ursprünglich auf einer Maschine befand. Führen Sie die Aufgabe auf jeder Maschine aus, teilen Sie die Aufgabe zunächst künstlich in mehrere Teile auf und lassen Sie alle Maschinen diese Person ausführen Cluster
Derzeit ist die Art der Verarbeitung geplanter Aufgaben im Cluster keine echte verteilte Verarbeitungsmethode, sondern eine pseudoverteilte Methode (im Unternehmen allgemein als native Methode bekannt). Methode besteht darin, dass bei einem Ausfall der Maschine im Cluster die gesamten geplanten Aufgaben hängen bleiben oder nicht auf einmal ausgeführt werden können, was schwerwiegende Auswirkungen auf das Unternehmen haben wird
Lösungen für Fehler (der Schwerpunkt dieses Artikels). )
Verwenden Sie Spring+Quartz, um ein echtes System für verteilte geplante Aufgaben einzurichten. Nachdem ich relevante Informationen konsultiert habe, habe ich erfahren, dass das Quartz-Framework verteilte geplante Aufgaben nativ unterstützt
Entwicklungs-IDE: Intellij IDEA
JDK-Version: 1.8
Frühlingsversion: 4.2.6
Quarzversion: 2.2.1
Spring- und Quarz-Integrationskonfiguration
Quarz-Eigenschaftendatei<?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>Beschreibung der zugehörigen Klasse
#============================================================================ # 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
AutowiringSpringBeanJobFactory-Klasse dient zur Verwendung von Federanmerkungen im Scheduler. Wenn Sie keine Anmerkungen verwenden, können Sie diese nicht anwenden Diese Klasse und verwenden Sie
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; } }Testergebnisse:
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())); } }
Da es nur einen Computer gibt, habe ich zum Testen die beiden Ports 8080 und 8888 geöffnet Geplante Aufgabe, die alle 10 Sekunden ausgeführt wird.
Wenn ich nur Port 8080 starte, sieht man, dass die Konsole alle 10 Sekunden eine Abrechnung druckt
In einem Vergleichstest, bei dem zwei Ports gestartet werden Gleichzeitig ist ersichtlich, dass nur ein Port die geplante Aufgabe ausführt.
Nach dem Schließen des Ports, auf dem die geplante Aufgabe ausgeführt wird, wird der andere Port geschlossen, der zuvor nicht ausgeführt wurde beginnt zu übernehmen und läuft weiter.
An diesem Punkt können wir deutlich erkennen, dass in einer verteilten geplanten Aufgabe (oder einem Cluster) nur eine geplante Aufgabe ausgeführt wird gleichzeitig laufen.
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er zum Lernen aller beiträgt. Ich hoffe auch, dass jeder die PHP-Chinesisch-Website unterstützt.
Weitere Artikel zur Implementierung des verteilten Timing-Task-Frameworks auf Basis von Spring+Quartz finden Sie auf der chinesischen PHP-Website!