`

Java Web应用中的任务调度

    博客分类:
  • Web
阅读更多
Java Web应用中的任务调度


      任务调度是大型J2EE web应用中常见的工作。开发者希望以指定的间隔时间执行各类操作,并完成一些无需用户输入的任务。Java中可有无数方法来做到这一点,但是在web应用中却并没有这方面的统一标准。当许多开发人员参与同一个项目,并且以各自不同的方式来实现任务调度时,就可能产生很大问题。内存和同步问题就是必须首先考虑的两件事。事实上,一些开发者试图调用操作系统层面的任务调度机制,如Unix平台上的cron。这种编程实践也许并不是太坏,但它将直接导致可移植性被抛到九霄云外。
  为何需要任务调度?

  在web应用中,大多数任务是以一种"防止用户长时间等待"的方式完成的。在Google搜索这样的例子中,减少等待时间对用户体验来说至关重要。异步任务的一种解决方案是在用户提交后生成一个线程(来处理异步任务),但这也不能解决那些需要以一定时间间隔重复运行任务、或在每天的指定时间运行任务的情况。

  让我们从一个数据库报表的例子来看看任务调度能如何帮助改善系统设计。报表可能是错综复杂的,这取决于用户所需数据的种类,以及是否需要从一个或多个数据库收集大量数据。用户可能需要很长时间来运行这样的"按需"报表。因此,我们向这个报表示例中添加任务调度机制,以便用户可以安排在任何他们需要的时间生成报表,并以PDF或其他格式在email中发送。用户可以让报表在每天的凌晨2:22,系统正处于低负荷时运行;也可以选择只在特定时间运行一次。通过在报表应用中加入任务调度,我们可以为产品添加一项有用的功能,并改善用户体验。

  幸运的是,有一个强大的开源解决方案可以让我们以标准的方式在web应用(或任何Java应用)中实施任务调度。以下示例展示了在web应用中,如何使用Quartz来创建一个任务调度框架。这个示例还使用了Struts Action framework 插件,以便在web应用启动时初始化任务调度机制。Struts是最常见的MVC框架,为大多数开发人员所熟悉。当然除此之外还有许多框架可以协助在web应用中实现MVC模式。

  启动时初始化任务调度器

  我们首先要做的是建立一个Struts插件,让它在容器启动时创建我们的任务调度器。在以下例子中,我们选择Tomcat作为web应用容器,不过这些示例在其他容器中也应当可以运行。我们要创建一个Struts插件类,并在struts-config.xml中加入几行代码以使之可以工作。

  这个插件有两个可配置的初始化参数:startOnLoad指定是否要在容器启动时立即启动任务调度器,而 startupDelay指定启动任务调度器之前的等待时间。启动延时很有用,因为我们可能需要首先执行一些更重要的初始化步骤。此外还可以使用listener机制,以更复杂的方式来通知SchedulerPlugIn何时启动Quartz Scheduler。

  <plug-in className="SchedulerPlugIn">
     <set-property property="startOnLoad" value="false" />
     <set-property property="startupDelay" value="0" />
  </plug-in>


  我们要创建的是一个实现Struts插件接口org.apache.struts.action.PlugIn的单子类SchedulerPlugIn。Struts会按照配置文件中出现的顺序初始化各个插件。要特别注意的是init()方法中的代码,在此我们初始化了所需的Quartz对象,并得到Scheduler。我们的任务信息就要提交到此org.quartz.Scheduler对象,后者将在随后讨论。Scheduler对象由Quartz servlet根据其配置初始化,就像Struts初始化它的ActionServlet类一样。让我们来看init()方法:

public void init(ActionServlet actionServlet,
                 ModuleConfig moduleConfig) {

  System.out.println("Initializing Scheduler PlugIn for Jobs!");
  // Retrieve the ServletContext
// 获取ServletContext
  ServletContext ctx = actionServlet.getServletContext();
  // The Quartz Scheduler
  // Quartz Scheduler对象
  Scheduler scheduler = null;

  // Retrieve the factory from the ServletContext.
  // It will be put there by the Quartz Servlet
  // 从ServletContext取得由Quartz Servlet放置在此的factory对象。
  StdSchedulerFactory factory =  (StdSchedulerFactory)
      ctx.getAttribute(QuartzInitializerServlet.QUARTZ_FACTORY_KEY);
   
  try{
        // Retrieve the scheduler from the factory
        // 从factory取得scheduler
        scheduler = factory.getScheduler();

        // Start the scheduler in case, it isn't started yet
        // 如果scheduler尚未启动,则启动它
        if (m_startOnLoad != null &&
            m_startOnLoad.equals(Boolean.TRUE.toString())){
            System.out.println("Scheduler Will start in " +
            m_startupDelayString + " milliseconds!");
            //wait the specified amount of time before
            // starting the process.
            // 在启动之前等待指定长度的时间
            Thread delayedScheduler =
                new Thread(new DelayedSchedulerStarted (
                                   scheduler, m_startupDelay));
            //give the scheduler a name. All good code needs a name
            //给任务调度器命名。好的代码总该有名字!
            delayedScheduler.setName("Delayed_Scheduler");
            //Start out scheduler
            //启动任务调度器
            delayedScheduler.start();
        }
  } catch (Exception e){
     e.printStackTrace();
  }
    sm_scheduler = scheduler;
}



配置过程的第二步是在web.xml中加入用来初始化Quartz servlet(org.quartz.ee.servlet.QuartzInitializerServlet)的内容,因为需要它将SchedulerFactory添加到ServletContext中,以便在我们的Struts插件中可以访问。SchedulerFactory就是我们在Struts插件中获得Scheduler对象的来源。除了struts-config.xml 和web.xml之外,还要在web应用的classes目录下放置一个quartz.properties文件。此文件的位置也可以在web.xml中作为QuartzInitializerServlet的启动参数来指定。
  <servlet>

     <servlet-name>QuartzInitializer</servlet-name>
     <display-name>Quartz Initializer Servlet</display-name>

  <servlet-class>
    org.quartz.ee.servlet.QuartzInitializerServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
  <init-param>
    <param-name>shutdown-on-unload</param-name>
    <param-value>true</param-value>
  </init-param>

  <init-param>
    <param-name>start-scheduler-on-load</param-name>
    <param-value>false</param-value>
    </init-param>

</servlet>



  这里其实完全可以不使用Struts和SchedulerPlugIn,但如果将来决定要以其它的任务调度框架替换Quartz的话,额外的抽象层就很有用了。长远看来,让一切保持松散耦合总会使工作变得容易些。如果你使用其它MVC框架,也可以用SchedulerPlugIn.init()方法中的代码达到同样的效果。此外,还可以用Servlet 2.3规范中的ServletContextListener来实现同样的初始化过程。

  到此为止web应用已配置完毕,我们可以创建一个.war文件并部署到服务器上,从控制台观察SchedulerPlugIn的输出信息。然而在此之前,让我们先看看如何向任务调度器提交一项任务。

  我们可以从web应用中的任何类访问SchedulerPlugIn的唯一实例,并调度一些要执行的工作。首先需要一个Trigger(触发器)对象来告诉任务何时运行、每隔多久运行一次。Quartz支持多种触发器,在这个例子中我们使用CronTrigger。

Trigger trigger = new CronTrigger("trigger1", "group1");
trigger.setCronExpression("0 0 15 ? * WED");


  以上的触发器会在每周三的下午3点执行指定任务。现在我们只要创建一个JobDetail对象,并把它和上面的触发器一起传递给SchedulerPlugIn的scheduleWork()方法。

    JobDetail jobDetail =
        new JobDetail("Hello World Job",
                      "Hello World Group",
                      HelloWorld.class,
                      true, true, true);   
//Schedule The work
//调度这项任务
    SchedulerPlugIn.scheduleWork(scheduledJobDetail, trigger);


  实际工作在何处?

  至此我们已决定Trigger,可以开始调度工作了。看上去一切都已完成,但实际上我们只是调度了一项任务,还有最重要的一步有待完成。注意HelloWorld.class作为参数传递给了JobDetail的构造函数。这个类就是实际完成工作的地方。HelloWorld继承了Quartz的Job类,并覆盖了execute()方法。当任务管理器决定运行这个任务时,execute()方法将被调用。来看代码:

import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;

//extend the proper Quartz class
//继承适当的Quartz类
public class HelloWorld extends Job {

//override the execute method
//覆盖execute方法
    public void execute(JobExecutionContext context) {

// Every job has it's own job detail
//每个Job都有独立的JobDetail
    JobDetail jobDetail = context.getJobDetail();
// The name is defined in the job definition
//name在Job定义中指定
    String jobName = jobDetail.getName();
//Every job has a Job Data map for storing extra information
//每个Job都有一个Job Data map来存放额外的信息
    JobDataMap dataMap = jobDetail.getJobDataMap();
      
       System.out.println("Hello World!!!");
   }
}



出于测试的目的,你可能希望将触发器的频率调的高一点,以便观察到HelloWorld的动作。毕竟,你不想一直等到凌晨2点才能确定调度的任务确实运行了。相反,你可能需要一个每隔10秒运行的触发器:
Trigger trigger = new SimpleTrigger("trigger1", "group1");
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(10000L); // milliseconds毫秒


  注意,这个触发器没有使用类cron的语法。Quartz有大量各类的选项和配置方法,可适用于任何任务调度的需要。

  其它计时方式的配置

  Quartz提供了多种调度任务的方式。CronTrigger可能是最复杂的一种,不过还有其它的选择。大多数触发器可以由Quartz提供的TriggerUtils类创建。以下是一些常见的触发器的例子。如谚语所言,条条大路通罗马!

  每天凌晨2:22触发的触发器

  // 方法一:使用makeDailyTrigger

Trigger trigger = TriggerUtils.makeDailyTrigger(2, 22);
trigger.setName("trigger1");
trigger.setGroup("group1");


  // 方法二:使用CronTrigger

Trigger trigger = new CronTrigger("trigger1", "group1");
trigger.setCronExpression("0 22 2 * * ?");


每5秒执行一次的触发器
/*  *
* 方法一:makeSecondlyTrigger
* 注意以下代码将创建一个立即启动的触发器。要控制启动时间,使用
* trigger.setStartTime(Date)方法。
*/
Trigger trigger = TriggerUtils.makeSecondlyTrigger(5);
trigger.setName("MyFiveSecondTrigger");
trigger.setGroup("MyTriggerGroup");

/*
*
* 方法二:设置SimpleTrigger的重复次数和间隔时间。
* 注意以下代码将创建一个立即启动的触发器。要控制启动时间,使用
* trigger.setStartTime(Date)方法。
*/
Trigger trigger = new SimpleTrigger("trigger1", "group1");
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(5000L); // milliseconds



  按间隔时间运行任务

Trigger trigger = new SimpleTrigger("trigger1", "group1");
// 24 hours * 60(minutes per hour) *
// 60(seconds per minute) * 1000(milliseconds per second)
// 24小时 * 60(分钟每小时) * 60(秒每分钟)* 1000(毫秒每秒钟)
trigger.setRepeatInterval(24L * 60L * 60L * 1000L);


  结论

  在这个演示中,我们只接触了Quartz框架的一些初级功能。记住,Java 5 和J2EE 5也有自己的任务调度机制,但是它们不像Quartz那样灵活易用。Quartz是目前唯一的开源Java任务调度框架,它的确为开发者的锦囊中增加了很有用的内容。你可从Open Symphony下载Quartz,并得到一份很好的教程和使用说明。
分享到:
评论

相关推荐

    在Java Web应用中如何实现任务有效调度

    在Java Web应用中,任务调度是一项关键功能,它允许开发者安排任务在特定时间或周期性地执行,以优化系统性能并提升用户体验。特别是在处理大数据、复杂计算或需要定期更新的场景下,如生成数据库报表,任务调度尤为...

    Java Web应用开发项目教程(附电子教案,程序源代码,习题答案)

    7. **数据库连接与JDBC**:在Java Web应用中,通常需要与数据库进行交互。掌握数据库的基本操作,如SQL查询、事务处理,以及使用JDBC(Java Database Connectivity) API建立数据库连接、执行SQL语句和处理结果集,...

    Java任务调度.pdf

    在现代信息系统中,尤其是Web应用中,用户体验往往取决于系统的响应速度。然而,诸如工资计算、大数据处理等任务可能涉及庞大的数据集和复杂的计算,如果在用户请求时即时处理,不仅会显著增加响应时间,还可能导致...

    java web定时任务 实例

    Java Web定时任务是Web应用程序中实现周期性操作的关键技术,例如数据备份、清理过期记录、发送邮件通知等。在本实例中,我们将探讨如何在Java Web环境中设置和执行定时任务,以及涉及到的相关技术和工具。 首先,...

    SSM任务调度demo

    SSM(Spring、SpringMVC、MyBatis)是一个常见的Java Web开发框架组合,而"SSM任务调度demo"则是基于这个框架实现的一个任务调度示例。在这个项目中,主要运用了Quartz作为任务调度的核心组件。Quartz是一个强大且...

    java web使用监听器实现定时周期性执行任务demo

    在Java Web应用程序中,监听器是实现了特定接口的类,它们可以在特定事件发生时被调用。例如,ServletContextListener可以在Web应用启动和停止时执行代码,HttpSessionListener可以监听会话的创建和销毁。在这个场景...

    如何在Web应用中去启动后台的任务

    在Java Web应用中,我们可以实现`ServletContextListener`接口来创建一个监听器类。这个接口有两个关键方法:`contextInitialized`和`contextDestroyed`。当Web应用启动时,容器会调用`contextInitialized`方法,而...

    Java开源的分布式任务调度平台 xxl-job.zip

    《分布式任务调度平台XXL-JOB》 一、简介 1.1 概述 XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。 1.2...

    java轻松实现—定时任务

    总结来说,Java实现定时任务有多种方法,其中在Web应用中使用监听器和`TimerTask`是一种常见的方式。这种方式易于理解和实现,但可能不适合复杂的定时需求。对于更复杂的需求,可以考虑使用`...

    Java任务调度框架Quartz2.0.2教程实例源代码

    Java任务调度框架Quartz2.0.2是一个强大的开源任务调度库,被广泛应用于各种Java应用程序中,包括Web应用和企业级系统。Quartz允许开发者安排任务以在特定时间执行,提供灵活的时间触发器和可插拔的Job执行策略。在...

    Spring定时任务实现(非Web项目)

    1. **配置Spring Task**:在Spring的配置文件(如`applicationContext.xml`或使用Java配置类)中,我们需要启用任务调度功能并配置相应的执行器或调度器。例如,使用Java配置可以这样写: ```java @Configuration @...

    NanoFramework一个用于快速开发Web及任务调度项目的框架

    NanoFramework是一个专为简化Web应用和任务调度项目开发而设计的高效框架,尤其适用于Java开发环境。这个框架的独特之处在于其精简的配置要求,使得开发者可以迅速搭建项目,节省了大量时间,提高了开发效率。 在...

    java web定时器例子

    Java Web定时器主要指的是在Java Web应用程序中实现定时任务的功能,这在许多业务场景中都是必要的,例如数据备份、报表生成、邮件发送等。在Java中,我们可以利用内置的`java.util.Timer`类或者Spring框架的`@...

    C#定时调度任务服务

    在这个类中,我们将初始化Quartz.NET的Scheduler,并在其Start方法中启动Scheduler,确保服务启动时任务调度也开始工作。同时,我们还需要实现Stop方法,用于在服务停止时关闭Scheduler。 Log4Net是Apache的一个...

    Java语言定时调度任务之实现.pdf

    定时调度任务在现代Web应用中是不可或缺的功能,尤其在需要周期性执行任务的各种业务系统中显得尤为重要。 Java语言中实现定时调度任务通常有三种主流方法:Timer调度、线程池调度和Quartz调度。每种方法各有其特点...

    基于java的任务调度 jconch.zip

    本篇将详细介绍JConch在Java任务调度中的应用及其核心特性。 1. **JConch简介** JConch是一个轻量级的任务调度库,它旨在简化Java应用程序中的定时任务管理。JConch设计灵感可能来源于开源项目Quartz,但更注重...

    基于Java的任务调度 jconch.zip

    jconch是一个基于Java的任务调度框架,它的设计目标是提供一个高效、稳定、易用的解决方案,用于在分布式环境中执行定时任务。在本篇文章中,我们将深入探讨Java任务调度的核心概念,以及jconch如何实现这些功能。 ...

    分布式任务调度

    分布式任务调度是现代大型互联网应用中的重要组成部分,它允许我们将工作负载分布到多个计算节点上,以实现更高效、更灵活的资源利用。XXL-JOB是一个专为此目的设计的轻量级分布式任务调度框架。它以其快速的开发...

    JBPM3.2与Spring结合时任务调度的实现.doc

    在Web应用的`web.xml`配置文件中,你需要添加一个名为`JobExecutorServlet`的Servlet来处理任务调度。这个Servlet的类是`org.jbpm.job.executor.JobExecutorServlet`,并设置加载优先级为3,确保在Web应用启动时能够...

    自己动手写任务调度平台项目源代码

    在IT领域,任务调度平台是不可或缺的一个组成部分,它在分布式系统、大数据处理、自动化运维等领域发挥着关键作用。...通过自己动手编写任务调度平台,不仅可以提升技术能力,还能更好地理解和应用这些理论知识。

Global site tag (gtag.js) - Google Analytics