Sring核心技术与最佳实践- 9.2 集成任务调度服务

2024-03-09 01:08

本文主要是介绍Sring核心技术与最佳实践- 9.2 集成任务调度服务,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Sring核心技术与最佳实践 第9章 9.2 集成任务调度服务

 

9.2 集成任务调度服务

 

并发所有的任务都是有用户发启请求而产生的,大多数系统都需要周期性地运行一些调度任务,比如,需要定时分析每天的日志记录,然后自动发送报告给系统管理员。要在JavaEE应用程序中实现这些调度任务,Spring提供了一个非常实用的调度器,可以方便的配置任务并定期执行。

根据Spring“不重新发明轮子”的原则,Spring框架没有自己实现调度器,而是提供了一个抽象层,它封装了JDK的Timer类和开源的Quartz调度器。

 

 

9.2.1 实用Timer调度任务

 

从JDK1.3 开始,提供了一个Timer类,可以实现周期性地执行某个任务,Spring对其包装为ScheduledTimerTask,设置起来非常简单。

为了能让系统周期性的分析日志,并发送邮件给管理员,在Eclipse中建立ReportTimerTask工程。

 

首先,需要定义一个任务,它派生自TimerTask。

public class ReportTimerTask extends TimerTask{

 

    @Override

    public void run() {

        String log = "read from log file at " +new Date();

        System.out.println(log);

        // analyse...

        try {

            Thread.sleep(1000);

        }

        catch(InterruptedException e) {

            e.printStackTrace();

        }

        // send mail...

    }

}

 

 

我们用Thread.sleep()模拟一个耗时的任务。至于发送邮件,前面有些文章有介绍,再此不说了,为了ReportTimerTask注入一个定义好的JavaMailSender对象,读者可以自己添加相关的依赖注入属性,完整地实现发送邮件的功能。

 

 

下一步是在XML配置文件中定义好reportTask和scheduledTask,其配置都相当的简单。

 

<!-- 单一任务 -->

<beanid="reportTask"class="com.zsw.timertask.ReportTimerTask"/>

 

<!-- 周期性任务 -->

<beanid="scheduledTask"class="org.springframework.scheduling.timer.ScheduledTimerTask">

    <!-- 启动后等待10秒,然后开始执行 -->

    <propertyname="delay"value="10000"/>

    <!-- 60秒执行一次 -->

    <propertyname="period"value="1000"/>

    <propertyname="timerTask"ref="reportTask"/>

</bean>

 

reportTask定义了一个独立的任务,而scheduledTask定义了周期性的任务。最后,通过定义一个timerFactory来启动这个周期性任务。

 

<!-- 启动任务的工厂 -->

<beanid="timerFactory"class="org.springframework.scheduling.timer.TimerFactoryBean">

    <propertyname="scheduledTimerTasks">

        <list>

            <refbean="scheduledTask"/>

        </list>

    </property>

</bean>

 

在测试程序中,我们启动Spring容器,然后等待5分钟结束程序,

public static void main(String[] args) {

    BeanFactory factorynew ClassPathXmlApplicationContext("config.xml");

    // 等待5分钟,观察输出:

    try {

        Thread.sleep(3000000);

    }

    catch(InterruptedException e) {}

    System.exit(0);

}

 

观察输出,可以看到,从容器启动后10秒开始,ReportTask以1分钟的间隔周期性运行。

 

如果志向运行一次任务,可以设定period的值为0。

另一个创建ScheduledTask的方法更简单,即使用MethodInvokingTimerTaskFactoryBean,它甚至不需要reportTask扩展TimerTask,只需要指定方法名称即可。

 

<beanid=""class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">

    <propertyname="targetObject"ref="reportTask"/>

    <propertyname="targetMethod"value="run"/>

</bean>

 

注意:这个MethodInvokingTimerTaskFacotryBean是一个FactoryBean,因此Spring容器创建的实际对象类型是ScheduledTimerTask。

 

由于JDK提供的Timer功能有限,只能以固定的周期运行任务。如果希望以更灵活的方式运行任务,例如,希望每天凌晨3:00向管理员发送报告,每周星期一至星期五早上6:00向用户发送新闻,可以使用Quartz来实现更复杂的调度任务。

 

 

9.2.2 使用Quartz调度任务

 

Quartz是一个功能极为强大的任务调度器,可以任意指定周期性的任务,Quartz还支持将任务存储到数据库中,这样即使重启服务器也可以继续上次没有执行的任务。要使用Quartz,请从http://quartz-scheduler.org/downloads/catalog下载最新版本。

 

 

Quartz实现任务调度的几个关键概念如下:

(1)      Job:定义一个任务,Job只管执行,不管什么时候执行,也不管执行多少次。

(2)      Trigger:定义一个触发器,表示应当如何触发一个Job的执行。Quartz提供了许多简单的Trigger,可以实现某一时刻触发、周期性触发等多种触发方式,并且一个Job可以和多个Trigger关联,这样能更加灵活地执行Job。

(3)      Scheduler:真正调度任务的调度器,通过scheduleJob(Job,Trigger)方法就把一个关联了Trigger的Job对象放入了调度器中执行。

 

用Quartz实现了一个Timer版本的日志发送童谣非常简单,却可以设置更多的调度方式。在Eclipse中建立了ReportQuartzTask工程。

 

 

Spring提供了两种方式来封装Quartz的Job,一种是直接派生自QuartzJobBean。例如,定义了一个Report对象向管理员发送报告,并且可以注入管理员的名字。

public class Report extends QuartzJobBean {

 

    private Stringname;   

    public void setName(String name) {

        this.name = name;

    }

 

    @Override

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

        System.out.println("Send report to " +name +" at " +new Date());

        try {

            Thread.sleep(1000);

        }catch(InterruptedException e) {

            e.printStackTrace();

        }

    }

}

 

在XML配置文件中将Report包装秤JobDetailBean

 

<beanname="reportJob"class="org.springframework.scheduling.quartz.JobDetailBean">

    <propertyname="jobClass"value="com.zsw.quartz.Report"/>

    <propertyname="jobDataAsMap">

        <map>

            <entrykey="name"value="Micheal"/>

        </map>

    </property>

</bean>

 

Spring会自动将jobDataAsMap属性中注入的Map按照key注入到Report对象的响应的属性中。

 

另一种方式是完全用最简单的POJO实现,例如,设计一个检查磁盘剩余空间的CheckDiskFreeSpace对象。

public class CheckDiskFreeSpace {

 

    public void check() {

        // get disk free space:

        long freeSpace = Math.random() > 0.5 ? 100000000 : 200000000;

        System.out.println("Check disk free space at " +new Date());

        if(freeSpace<100*1024*1024) {// <100MB

            System.out.println("Warning! Low disk free space...");

        }

    }

}

 

Check()方法是执行方法,下一步是在XML配置文件中将其也包装为 JobDetailBean。

<beanname="checkDiskFreeSpace"class="com.zsw.quartz.CheckDiskFreeSpace"/>

 

<beanname="checkDiskJob"class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

    <propertyname="targetObject"ref="checkDiskFreeSpace"/>

    <propertyname="targetMethod"value="check"/>

    <propertyname="concurrent"value="false"/>

</bean>

 

concurrent属性指定了这个checkDiskJob是否能同时执行,默认为true;若设置为fasle,如果当前有一个checkDiskJob正在执行,则不启动相同的checkDiskJob。如果一个 Job执行时间较长,执行频率有比较高,定义concurrent为false可以避免多个相同的Job同时执行。

下一步是定义Trigger,它定义了在何时触发Job的执行。Spring封装了两种常用的TriggerBean,一种是SimpleTriggerBean,可以周期性的执行Job,为了让checkDisJob每个5分钟执行一次,配置如下:

<!-- 周期性运行checkDiskJob -->

<beanid="repeatTrigger"class="org.springframework.scheduling.quartz.SimpleTriggerBean">

    <propertyname="jobDetail"ref="checkDiskJob"/>

    <!-- 1分钟后启动 -->

    <propertyname="startDelay"value="60000"/>

    <!—1秒检查一次 -->

    <propertyname="repeatInterval"value="1000"/>

</bean>

 

另一种CroTriggerBean功能更为强大,能指定何时执行Job。例如,希望reportJob在周一至周五中午12:00执行,就可以在XML配置文件中按如下方式定义。

<!-- 定时运行reportJob -->

<beanid="cronTrigger"class="org.springframework.scheduling.quartz.CronTriggerBean">

    <propertyname="jobDetail"ref="reportJob"/>

    <!-- 每周周一至周五中午12:00执行 -->

    <propertyname="cronExpression"value="0 0 12 ? * MON-FRI"/>

</bean>

 

cronExpression表达式定义了Job的执行规则,稍后我们将详细讨论。现在,我们希望启动这些定义好的Job,在XML配置文件中定义一个Scheduler,然后将所有的TriggerBean都注入进去即可。

 

<!-- 启动调度器 -->

<beanid="scheduler"class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

    <propertyname="triggers">

        <list>

            <refbean="repeatTrigger"/>

            <refbean="cronTrigger"/>

        </list>

    </property>

</bean>

 

最后,编写main()方法启动Spring容器。

public class Main {

    public static void main(String[] args) {

        new ClassPathXmlApplicationContext("config.xml");

    }

}

 

运行程序,观察输出.

Check disk free space at Sat Apr 21 01:37:34 CST 2012

Check disk free space at Sat Apr 21 01:37:35 CST 2012

Check disk free space at Sat Apr 21 01:37:36 CST 2012

Warning! Low disk free space...

 

可以看到checkDiskJob按照1秒的间隔运行,而reportJob在周二的中午12:00执行了!为了能看到执行效果,你可能需要修改执行时间,否则最长要等到24小时才能看到reportJob第一次被执行。

 

现在我们来讨论一下cronExpression的写法。cronExpression用于定义触发Job的时间,由7各部分组成,分别代表:

(1)   秒;

(2)   分;

(3)   小时;

(4)   一月中的某天;

(5)   月份;

(6)   星期;

(7)   年份(可选,通常不用指定,因为以年为周期的执行任务通常不需要Quartz来完成)

 

例如,“0 0 12 ? * MON”表示周一12:00:00执行,?表示忽略月份中的天数,因为通常要么按照日期执行,要么按照星期指定,*表示全部,即每月都执行。

 

可以使用“,”指定一个列表,例如,“0 0 6 1,3,5 * ?”表示每月1号、3号和5号的早上6:00执行,还可以使用“-”指定一个范围,例如,“0 0 6 ? * MON-FRI”表示周一至周五早上6:00执行。

 

可以使用“/”指定时间间隔,例如,设置分钟为“0/15”表示从0分钟开始,每隔15分钟执行一次,而”3/20”表示从3分钟开始,每隔20分钟执行一次,这和“3,23,43”的效果是完全一样的。

 

以下是一些常用的表达式。

“0 0/5 * * * ?”表示每隔5分钟执行一次;

“10 0/5 * * *?”表示每隔5分钟,在10秒时执行,例如:00:10、05:10;

“0 30 10-20 ?* WED,FRI”表示每周三和每周五在10:30、11:30、12:30执行;

“0 0/30 9-171-5,10 * ?” 表示在每月1号到5号,以及10号这几天,每天从9:00到17:00每隔30分钟执行一次;

 

一些更复杂的调度可能无法以一个cronExpression表示出来,此时,可以考虑将其分拆为几个cronExpression,然后将这几个CrontriggerBean都添加到Scheduler中。


代码下载地址:http://download.csdn.net/detail/sz_bdqn/4243746


 

这篇关于Sring核心技术与最佳实践- 9.2 集成任务调度服务的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/789053

相关文章

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C++学习 贝蒂的主页:Betty’s blog 1. 泛型编程 首先让我们来思考一个问题,如何实现一个交换函数? void swap(int& x, int& y){int tmp = x;x = y;y = tmp;} 相信大家很快就能写出上面这段代码,但是如果要求这个交换函数支持字符型

SpringBoot集成Netty,Handler中@Autowired注解为空

最近建了个技术交流群,然后好多小伙伴都问关于Netty的问题,尤其今天的问题最特殊,功能大概是要在Netty接收消息时把数据写入数据库,那个小伙伴用的是 Spring Boot + MyBatis + Netty,所以就碰到了Handler中@Autowired注解为空的问题 参考了一些大神的博文,Spring Boot非controller使用@Autowired注解注入为null的问题,得到

亮相WOT全球技术创新大会,揭秘火山引擎边缘容器技术在泛CDN场景的应用与实践

2024年6月21日-22日,51CTO“WOT全球技术创新大会2024”在北京举办。火山引擎边缘计算架构师李志明受邀参与,以“边缘容器技术在泛CDN场景的应用和实践”为主题,与多位行业资深专家,共同探讨泛CDN行业技术架构以及云原生与边缘计算的发展和展望。 火山引擎边缘计算架构师李志明表示:为更好地解决传统泛CDN类业务运行中的问题,火山引擎边缘容器团队参考行业做法,结合实践经验,打造火山

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

springboot家政服务管理平台 LW +PPT+源码+讲解

3系统的可行性研究及需求分析 3.1可行性研究 3.1.1技术可行性分析 经过大学四年的学习,已经掌握了JAVA、Mysql数据库等方面的编程技巧和方法,对于这些技术该有的软硬件配置也是齐全的,能够满足开发的需要。 本家政服务管理平台采用的是Mysql作为数据库,可以绝对地保证用户数据的安全;可以与Mysql数据库进行无缝连接。 所以,家政服务管理平台在技术上是可以实施的。 3.1

9 个 GraphQL 安全最佳实践

GraphQL 已被最大的平台采用 - Facebook、Twitter、Github、Pinterest、Walmart - 这些大公司不能在安全性上妥协。但是,尽管 GraphQL 可以成为您的 API 的非常安全的选项,但它并不是开箱即用的。事实恰恰相反:即使是最新手的黑客,所有大门都是敞开的。此外,GraphQL 有自己的一套注意事项,因此如果您来自 REST,您可能会错过一些重要步骤!

BD错误集锦8——在集成Spring MVC + MyBtis编写mapper文件时需要注意格式 You have an error in your SQL syntax

报错的文件 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yuan.dao.YuanUserDao"><!

BD错误集锦7——在集成Spring MVC + MyBtis时使用c3p0作为数据库时报错Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatem

异常信息如下: Type Exception ReportMessage Handler dispatch failed; nested exception is java.lang.AbstractMethodError: Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatement.isClosed()Z is abstractDescr

陀螺仪LSM6DSV16X与AI集成(8)----MotionFX库解析空间坐标

陀螺仪LSM6DSV16X与AI集成.8--MotionFX库解析空间坐标 概述视频教学样品申请源码下载开启CRC串口设置开启X-CUBE-MEMS1设置加速度和角速度量程速率选择设置FIFO速率设置FIFO时间戳批处理速率配置过滤链初始化定义MotionFX文件卡尔曼滤波算法主程序执行流程lsm6dsv16x_motion_fx_determin欧拉角简介演示 概述 本文将探讨

微服务中RPC的强类型检查与HTTP的弱类型对比

在微服务架构中,服务间的通信是一个至关重要的环节。其中,远程过程调用(RPC)和HTTP是两种最常见的通信方式。虽然它们都能实现服务间的数据交换,但在类型检查方面,RPC的强类型检查和HTTP的弱类型之间有着显著的差异。本文将深入探讨这两种通信方式在类型检查方面的优缺点,以及它们对微服务架构的影响。 一、RPC的强类型检查 RPC的强类型检查是其核心优势之一。在RPC通信中,客户端和服务端都使