Spring定时任务小结(spring schedule和Quartz)

Spring定时任务小结(spring schedule和Quartz)

Spring schedule

Spring中较为简单的任务调度处理方法,通过注解和Cron表达式对任务进行调度。可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

  1. 基于注解来设置调度器。
  2. 非常方便实现简单的调度
  3. 对代码不具有入侵性,非常轻量级

操作

导入依赖

xml文件配置

这里博主给出两种方式启动schedule

<?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"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task"
       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-4.0.xsd
                    http://www.springframework.org/schema/task
                    http://www.springframework.org/schema/task/spring-task.xsd">

    <!--第一种方式-->
    <context:component-scan base-package="com.cloneZjrt.schedule" />

    <!--第二种方式-->
    <!--<task:scheduler id="mySchedulerOne" pool-size="5"/>-->

    <!--<bean id="test" class="com.cloneZjrt.schedule.MyScheduler"></bean>-->

    <!--<task:scheduled-tasks scheduler="mySchedulerOne">-->
        <!--<task:scheduled ref="test" method="doSomething" cron="${schedule.task.doSomething}"/>-->
    <!--</task:scheduled-tasks>-->

    <!--可配置多个scheduler-->
    <!--<task:scheduler id="mySchedulerTwo"/>-->
    <!--<task:scheduled-tasks scheduler="myScheduler2">-->
        <!--<task:scheduled ref="myScheduler" method="doOtherThing" cron="0/5 * *  * * ?"/>-->
    <!--</task:scheduled-tasks>-->

</beans>

这里博主将所有的Cron表达式都放在schedule.properties这个文件中,这样易于修改

这个Cron表达式怎么写博主在这里就不介绍了,可参考博文cron表达式详解,也可使用自动生成Cron表达式的网站

schedule.task.doSomething=0/1 * * * * ?
schedule.task.doOtherThing=0/5 * * * * ?

spring-schedule.xmlschedule.properties文件导入主配置文件

<import resource="classpath*:/spring/spring-schedule.xml"/>

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:schedule.properties</value>
        </list>
    </property>
</bean>

任务类编写

第一种方式直接用@EnableScheduling@Scheduled注解,读取配置中的Cron语句,运行任务

第二种方式通过Bean,在xml文件中运行任务

注:选取一种方式运行,将另一种注释,不然会运行两次

@EnableScheduling
@Component
public class MyScheduler {

    @Scheduled(cron="${schedule.task.doSomething}")   //第一种方式
    public void doSomething() {

        System.out.println("do something:" + new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()));
    }

//    @Scheduled(cron="${schedule.task.doOtherThing}")
//    public void doOtherThing() {
//        System.out.println("do Otherthing:" + new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()));
//    }
}

Scheduler多实例场景下的问题

多实例的情况下,scheduler需要在多个实例上运行。大致有以下几种解决方案

  • 部署的时候,针对不同实例,使用不同的配置。

增加部署成本。

一个实例挂了,scheduler就挂了。

  • 在task的基类加入一些逻辑,当开始运行时,将状态(运行机器的IP、时间等)写入数据库、缓存或者zk,运行结束时重置状态,其它实例看到有这样的数据,就直接返回。

需要所有实例上的机器时间同步,不然一个刚结束另一个才开始,状态的维护就没有用了。

一定要保证结束运行后将状态重置,否则下一个运行周期,所有的task都会返回的。实在不行还得写一个task做这个事。

因为读写状态并非原子操作,偶尔也会发生task同时运行的事。

  • 将scheduler与web分开,这样还能避免后台任务影响web端。

增加部署成本。

scheduler的高可用需要重新考虑。

quartz

quartz 通过在数据库中配置定时器信息, 以数据库悲观锁的方式达到同一个任务始终只有一个节点在运行。

优点

  • 保证节点高可用 (HA), 如果某一个节点挂了, 其他节点可以顶上。

缺点

  • 同一个任务只能有一个节点运行,其他节点将不执行任务,性能低,资源浪费;

  • 当碰到大量短任务时,各个节点频繁的竞争数据库锁,节点越多这种情况越严重,性能会很低下;

  • quartz的分布式仅解决了集群高可用的问题,并没有解决任务分片的问题,不能实现水平扩展。

使用场景

在大众化产品,对分布式调度要求不高的产品可选择大面积使用。

Spring整合quartz

添加依赖

<!--Quartz依赖-->
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.0</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.2.6.RELEASE</version>
</dependency>

编写任务类

编写Job类,这里博主继承QuartzJobBean,当然实现Job也是可以的,QuartzJobBeanJob的实现类

@Data
public class MyQuartzJob extends QuartzJobBean {

    private int timeout;
    private String stringValue;

    //需要调度的任务
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date());
        System.out.println("MyQuartzJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));
    }

}

文件配置

使用spring-quartz.xml文件进行任务配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.cloneZjrt.util.MyJob"></property>
        <property name="durability" value="true"></property>
        <property name="jobDataAsMap">
            <map>
                <entry key="timeout" value="10" />
                <entry key="stringValue" value="value====" />
            </map>
        </property>
    </bean>

    <!--触发器配置-->
    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <!--入jobDetail-->
        <property name="jobDetail" ref="jobDetail" />
        <!--延迟开始-->
        <property name="startDelay" value="0" />
        <!--相隔时间-->
        <property name="repeatInterval" value="2000" />
        <!--重复次数-->
        <property name="repeatCount" value="10" />
    </bean>

    <!-- 总管理类 如果将lazy-init='false'那么容器启动就会执行调度程序 -->
    <bean id="startQuertz" lazy-init="false" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!-- 管理trigger -->
        <property name="triggers">
            <list>
                <!--任务列表-->
                <ref bean="simpleTrigger" />
            </list>
        </property>
    </bean>
</beans>

然后配置到applicationContext.xml

<import resource="classpath*:/spring/spring-quartz.xml"/>

main进行测试

public class MainTest {

    public static void main(String[] args) throws Exception{
        new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

当然,用web服务器部署项目后,自动加载xml文件并启动配置的任务。

发布了11 篇原创文章 · 获赞 1 · 访问量 491

猜你喜欢

转载自blog.csdn.net/xiaoHui_1126/article/details/104619829