web前端

位置:IT落伍者 >> web前端 >> 浏览文章

在Web工程中实现任务计划调度


发布日期:2022年11月09日
 
在Web工程中实现任务计划调度

好多朋友用过Windows的任务计划也有不少程序迷自己曾写过时钟报警系统自动关机等趣味程序可却很少有朋友在Web工程中实现过类似功能今天有空把笔者先前曾在Tomcat上实现的类似功能搬出来与大家共享

早在几年前我公司跟某市财政局合作项目开发为加强财政局对所属单位财务状况的有效监管开发实施了财政局数据中心项目此项目采用B/S加C/S混合结构模式财政局Web服务器上架设数据同步接收装置由市属单位每天下班前把财务信息通过HTTP协议上传至财政局中心服务器与Web服务器上的接收装置对接财政局内部各部门需要查阅大量财务信息获取完备的市属单位当前财务状况信息各部门按职能划分需要准确的获取各部门各自所关注的汇总信息以财政报表的形式提供

因财政数据量大实时计算财政报表速度较慢当初就考虑用报表缓存来减轻服务器的负担但用缓存需要一个合理的缓存更新机制考虑到各市属单位每天下班前才把财务数据上传财政局每天所查看到的财务信息其实并不包括当天(除非有某位领导等到所属单位全部上传完之后才来查看信息应该已经下班了)所以要是能实现任务计划调度在每晚深夜把当天及历史财务信息汇总更新缓存速度瓶颈不就解决了吗

当时由于系统核心是基于Web部署的报表计算引擎也相应的部署在Tomcat容器上因此如果想要借用Windows的任务计划来实现定时计算就需要额外编写普通桌面应用程序接口稍显复杂于是就琢磨着想在Web上实现经过查阅较多相关资料发现Java定时器(javautilTimer)有定时触发计划任务的功能通过配置定时器的间隔时间在某一间隔时间段之后会自动有规律的调用预先所安排的计划任务(javautilTimerTask)另外由于我们希望当Web工程启动时定时器能自动开始计时在整个Web工程的生命期里定时器能在每晚深夜触发一次报表计算引擎因此定时器的存放位置也值得考查不能简单的存在于单个Servlet或JavaBean中必须能让定时器宿主的存活期为整个Web工程生命期在工程启动时能自动加载运行结合这两点跟Servlet上下文有关的侦听器就最合适不过了通过在工程的配置文件中加以合理配置会在工程启动时自动运行并在整个工程生命期中处于监听状态

下面就Servlet侦听器结合Java定时器来讲述整个实现过程要运用Servlet侦听器需要实现javaxservletServletContextListener接口同时实现它的contextInitialized(ServletContextEventevent)和contextDestroyed(ServletContextEventevent)两个接口函数考虑定时器有个建立和销毁的过程看了前面两个接口函数就不容置疑的把建立的过程置入contextInitialized把销毁的过程置入contextDestroyed了

我把ServletContextListener的实现类取名为ContextListener在其内添加一个定时器示例代码如下所示(为考虑篇幅仅提供部分代码供读者参考)

    privatejavautilTimertimer=null;

    publicvoidcontextInitialized(ServletContextEventevent){

    timer=newjavautilTimer(true);

    eventgetServletContext()log(定时器已启动);

    timerschedule(newMyTask(eventgetServletContext())**);

    eventgetServletContext()log(已经添加任务调度表);

    }

    publicvoidcontextDestroyed(ServletContextEventevent){

    timercancel();

    eventgetServletContext()log(定时器销毁);

    }

以上代码中timerschedule(newMyTask(eventgetServletContext())**)这一行为定时器调度语句其中MyTask是自定义需要被调度的执行任务(在我的财政数据中心项目中就是报表计算引擎入口)从javautilTimerTask继承下面会重点讲述第三个参数表示每小时(即**毫秒)被触发一次中间参数表示无延迟其它代码相当简单不再详细说明

下面介绍MyTask的实现上面的代码中看到了在构造MyTask时传入了javaxservletServletContext类型参数是为记录Servlet日志方便而传入因此需要重载MyTask的构造函数(其父类javautilTimerTask原构造函数是没有参数的)在timerschedule()的调度中设置了每小时调度一次因此如果想实现调度任务每小时被执行一次还需要判断一下时钟点以常量C_SCHEDULE_HOUR表示(晚上也即点)同时为防止小时执行下来任务还未执行完(当然一般任务是没有这么长的)避免第二次又被调度以引起执行沖突设置了当前是否正在执行的状态标志isRunning示例代码如下所示

    privatestaticfinalintC_SCHEDULE_HOUR=;

    privatestaticbooleanisRunning=false;

    privateServletContextcontext=null;

    publicMyTask(ServletContextcontext){

    this}

    publicvoidrun(){

    Calendarcal=Calendar.getInstance();

    if(!isRunning){

    if(C_SCHEDULE_HOUR==cal.get(Calendar.HOUR_OF_DAY)){

    isRunning=true;

    context.log("开始执行指定任务");

    //TODO添加自定义的详细任务,以下只是示例

    inti=0;

    while(i++<10){

    context.log("已完成任务的"+i+"/"+10);

    }

    isRunning=false;

    context.log("指定任务执行结束");

    }

    }else{

    context.log("上一次任务执行还未结束");

    }

    }

上面代码中“//TODO……”之下四行是真正被调度执行的演示代码(在我的财政数据中心项目中就是报表计算过程),您可以换成自己希望执行的语句。TW.WINGWIT.cOm

到这儿,ServletContextListener和MyTask的代码都已完整了。最后一步就是把ServletContextListener部署到您的Web工程中去,在您工程的web.xml配置文件中加入如下三行:

com.test.ContextListener

当然,上面的com.test得换成您自己的包名了。保存web.xml文件后,把工程打包部署到Tomcat中即可。任务会在每晚12点至凌晨1点之间被执行,上面的代码会在Tomcat的日志文件中记录如下:

2003-12-050:21:39开始执行指定任务

2003-12-050:21:39已完成任务的1/10

……

2003-12-050:21:39已完成任务的10/10

2003-12-050:21:39指定任务执行结束

上一篇:用HTML编写的几个固定界面

下一篇:HBCZT信息中心Weblogic Server性能调优