Heim  >  Artikel  >  Java  >  Implementierung eines Frameworks für verteilte geplante Aufgaben basierend auf Spring+Quartz

Implementierung eines Frameworks für verteilte geplante Aufgaben basierend auf Spring+Quartz

高洛峰
高洛峰Original
2017-02-07 15:24:591353Durchsuche

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

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;
  }
}
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

Implementierung eines Frameworks für verteilte geplante Aufgaben basierend auf Spring+Quartz In einem Vergleichstest, bei dem zwei Ports gestartet werden Gleichzeitig ist ersichtlich, dass nur ein Port die geplante Aufgabe ausführt.

Implementierung eines Frameworks für verteilte geplante Aufgaben basierend auf Spring+Quartz 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.

Implementierung eines Frameworks für verteilte geplante Aufgaben basierend auf Spring+QuartzAn 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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn