Spring integrates Quartz distributed scheduling

Foreword
In order to ensure the high availability and high concurrency of the application, multiple nodes are generally deployed; for scheduled tasks, if each node executes its own scheduled tasks, on the one hand, system resources are consumed, and on the other hand, some tasks are executed multiple times. It may cause application logic problems, so a distributed scheduling system is needed to coordinate each node to perform scheduled tasks.

Spring integrates Quartz
Quartz is a mature task scheduling system. Spring is compatible with Quartz, which is convenient for development. Let's see how to integrate it:
1. Maven dependency file

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.3</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.29</version>
        </dependency>
    </dependencies>

The main ones are Spring related libraries, quartz libraries and mysql driver libraries. Note: The database is required for distributed scheduling, and mysql is used here;

2. Configuring the job
provides two ways to configure the job, namely: MethodInvokingJobDetailFactoryBean and JobDetailFactoryBean 2.1 MethodInvokingJobDetailFactoryBean is
used
when calling a method of a specific bean. The specific configuration is as follows:

<bean id="firstTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">  
    <property name="targetObject" ref="firstService" />  
    <property name="targetMethod" value="service" />  
</bea>

2.2JobDetailFactoryBean
is more flexible in this way, and can set parameters to pass, as follows:

<bean id="firstTask"
        class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="zh.maven.SQuartz.task.FirstTask" />
        <property name="jobDataMap">
            <map>
                <entry key="firstService" value-ref="firstService" />
            </map>
        </property>
</bean>

The task class defined by jobClass inherits QuartzJobBean and implements the executeInternal method; jobDataMap is used to pass data to the job;

3. Triggers used in configuration scheduling
also provide two types of triggers: SimpleTriggerFactoryBean and CronTriggerFactoryBean
, focusing on CronTriggerFactoryBean, this type is more flexible, as follows:

<bean id="firstCronTrigger"
    class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="firstTask" />
    <property name="cronExpression" value="0/5 * * ? * *" />
</bean>

jobDetail specifies the job configured in step 2, and cronExpression is configured to execute the job every 5 seconds;

4. The SchedulerFactoryBean that configures the Quartz scheduler
also provides two ways: memory RAMJobStore and database mode
4.1 Memory RAMJobStore
job related information is stored in memory, each node stores its own, isolated from each other, the configuration is as follows:

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="firstCronTrigger" />
        </list>
    </property>
</bean>

4.2 Database mode
Job related information is stored in the database, all nodes share the database, and each node communicates through the database to ensure that a job will only be executed on one node at the same time, and
if a node hangs, the job will be assigned Execute to other nodes, the specific configuration is as follows:

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/quartz" />
        <property name="user" value="root" />
        <property name="password" value="root" />
    </bean>
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:quartz.properties" />
        <property name="triggers">
            <list>
                <ref bean="firstCronTrigger" />
            </list>
        </property>
    </bean>

dataSource is used to configure data source, data table related information, you can go to the official website to download the gz package, the sql file is in the path: docs\dbTables , which provides the sql file of the mainstream database, a total of 11 tables;
quartz.properties file configured by configLocation Under the org.quartz package of quartz.jar, some default data are provided, such as org.quartz.jobStore.class

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

Here you need to copy quartz.properties and make some modifications. The specific modifications are as follows:

org.quartz.scheduler.instanceId: AUTO
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.isClustered: true
org.quartz.jobStore.clusterCheckinInterval: 1000

5. Related classes

public class FirstTask extends QuartzJobBean {
 
    private FirstService firstService;
 
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        firstService.service();
    }
 
    public void setFirstService(FirstService firstService) {
        this.firstService = firstService;
    }
}

FirstTask inherits QuartzJobBean, implements the executeInternal method, and calls FirstService;

public class FirstService implements Serializable {
 
    private static final long serialVersionUID = 1L;
 
    public void service() {
        System.out.println(new SimpleDateFormat("YYYYMMdd HH:mm:ss").format(new Date()) + "---start FirstService");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(new SimpleDateFormat("YYYYMMdd HH:mm:ss").format(new Date()) + "---end FirstService");
    }
}

FirstService needs to provide a serialization interface because it needs to be stored in the database;

public class App {
    public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartz.xml");
    }
}

The main class is used to load the quartz configuration file;

Test distributed scheduling
1. Start the App twice at the same time and observe the log:

20180405 14:48:10---start FirstService
20180405 14:48:12---end FirstService
20180405 14:48:15---start FirstService
20180405 14:48:17---end FirstService

Among them, A1 has log output, A2 does not; when A1 is stopped, A2 has log output;

2. Add new jobs and create them respectively: SecondTask and SecondService, add relevant configuration files at the same time, and start the App to observe the logs: A1 logs are as follows
:

20180405 15:03:15---start FirstService
20180405 15:03:15---start SecondService
20180405 15:03:17---end FirstService
20180405 15:03:17---end SecondService
20180405 15:03:20---start FirstService
20180405 15:03:22---end FirstService
20180405 15:03:25---start FirstService
20180405 15:03:27---end FirstService

The A2 log is as follows:

20180405 15:03:20---start SecondService
20180405 15:03:22---end SecondService
20180405 15:03:25---start SecondService
20180405 15:03:27---end SecondService

It can be found that both A1 and A2 have execution tasks, but the same task will only be executed on one node at the same time, and it is possible to assign it to other nodes only after the execution ends;

3. If the interval time is less than the task execution time, for example, change it to sleep(6000)
A1 log is as follows:

20180405 15:14:40---start FirstService
20180405 15:14:45---start FirstService
20180405 15:14:46---end FirstService
20180405 15:14:50---start FirstService
20180405 15:14:50---start SecondService
20180405 15:14:51---end FirstService

The A2 log is as follows:

20180405 15:14:40---start SecondService
20180405 15:14:45---start SecondService
20180405 15:14:46---end SecondService
20180405 15:14:51---end SecondService

The interval time is 5 seconds, and the task execution takes 6 seconds. By observing the log, it can be found that the task has not ended, and a new task has started. This situation may cause logical problems in the application, in fact, whether the task can support serialization;

4. The @DisallowConcurrentExecution annotation ensures the serialization of tasks.
Add the @DisallowConcurrentExecution annotations to FirstTask and SecondTask respectively. The log results are as follows: A1 The log is as follows
:

20180405 15:32:45---start FirstService
20180405 15:32:51---end FirstService
20180405 15:32:51---start FirstService
20180405 15:32:51---start SecondService
20180405 15:32:57---end FirstService
20180405 15:32:57---end SecondService
20180405 15:32:57---start FirstService
20180405 15:32:57---start SecondService

The A2 log is as follows:

20180405 15:32:45---start SecondService
20180405 15:32:51---end SecondService

Observing the log, it can be found that the task will start a new task only after the end, which realizes the serialization of the task;

Summary
This article aims to have an intuitive understanding of Spring+Quartz distributed scheduling, and solve problems through actual use. Of course, there may be many questions such as how it is scheduled, what happens if the database hangs, etc. Do a deeper understanding.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324378268&siteId=291194637