Spring定时任务小结(spring schedule和Quartz)
文章目录
Spring schedule
Spring
中较为简单的任务调度处理方法,通过注解和Cron表达式对任务进行调度。可以将它看成一个轻量级的Quartz
,而且使用起来比Quartz
简单许多。
- 基于注解来设置调度器。
- 非常方便实现简单的调度
- 对代码不具有入侵性,非常轻量级
操作
导入依赖
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.xml
和schedule.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
也是可以的,QuartzJobBean
是Job
的实现类
@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文件并启动配置的任务。