Java-Android-jwebee
          Java-Android-jwebee
          對(duì)IT人來(lái)說(shuō),要成為一個(gè)優(yōu)秀的技術(shù)型管理者,除了需要具備扎實(shí)的技術(shù)基礎(chǔ)之外,還應(yīng)該培養(yǎng)良好的人際關(guān)系能力、談判與溝通技能、客戶關(guān)系與咨詢技能、商業(yè)頭腦和財(cái)務(wù)技能以及創(chuàng)新意識(shí),此外還要有巧妙的激勵(lì)技巧和化解沖突與解決突發(fā)問(wèn)題的能力.
          來(lái)源:賽迪網(wǎng)

          任務(wù)調(diào)度是大型J2EE web應(yīng)用中常見(jiàn)的工作。開(kāi)發(fā)者希望以指定的間隔時(shí)間執(zhí)行各類操作,并完成一些無(wú)需用戶輸入的任務(wù)。Java中可有無(wú)數(shù)方法來(lái)做到這一點(diǎn),但是在web應(yīng)用中卻并沒(méi)有這方面的統(tǒng)一標(biāo)準(zhǔn)。當(dāng)許多開(kāi)發(fā)人員參與同一個(gè)項(xiàng)目,并且以各自不同的方式來(lái)實(shí)現(xiàn)任務(wù)調(diào)度時(shí),就可能產(chǎn)生很大問(wèn)題。內(nèi)存和同步問(wèn)題就是必須首先考慮的兩件事。事實(shí)上,一些開(kāi)發(fā)者試圖調(diào)用操作系統(tǒng)層面的任務(wù)調(diào)度機(jī)制,如Unix平臺(tái)上的cron。這種編程實(shí)踐也許并不是太壞,但它將直接導(dǎo)致可移植性被拋到九霄云外。

          為何需要任務(wù)調(diào)度?

          在web應(yīng)用中,大多數(shù)任務(wù)是以一種"防止用戶長(zhǎng)時(shí)間等待"的方式完成的。在Google搜索這樣的例子中,減少等待時(shí)間對(duì)用戶體驗(yàn)來(lái)說(shuō)至關(guān)重要。異步任務(wù)的一種解決方案是在用戶提交后生成一個(gè)線程(來(lái)處理異步任務(wù)),但這也不能解決那些需要以一定時(shí)間間隔重復(fù)運(yùn)行任務(wù)、或在每天的指定時(shí)間運(yùn)行任務(wù)的情況。

          讓我們從一個(gè)數(shù)據(jù)庫(kù)報(bào)表的例子來(lái)看看任務(wù)調(diào)度能如何幫助改善系統(tǒng)設(shè)計(jì)。報(bào)表可能是錯(cuò)綜復(fù)雜的,這取決于用戶所需數(shù)據(jù)的種類,以及是否需要從一個(gè)或多個(gè)數(shù)據(jù)庫(kù)收集大量數(shù)據(jù)。用戶可能需要很長(zhǎng)時(shí)間來(lái)運(yùn)行這樣的"按需"報(bào)表。因此,我們向這個(gè)報(bào)表示例中添加任務(wù)調(diào)度機(jī)制,以便用戶可以安排在任何他們需要的時(shí)間生成報(bào)表,并以PDF或其他格式在email中發(fā)送。用戶可以讓報(bào)表在每天的凌晨2:22,系統(tǒng)正處于低負(fù)荷時(shí)運(yùn)行;也可以選擇只在特定時(shí)間運(yùn)行一次。通過(guò)在報(bào)表應(yīng)用中加入任務(wù)調(diào)度,我們可以為產(chǎn)品添加一項(xiàng)有用的功能,并改善用戶體驗(yàn)。

          幸運(yùn)的是,有一個(gè)強(qiáng)大的開(kāi)源解決方案可以讓我們以標(biāo)準(zhǔn)的方式在web應(yīng)用(或任何Java應(yīng)用)中實(shí)施任務(wù)調(diào)度。以下示例展示了在web應(yīng)用中,如何使用Quartz來(lái)創(chuàng)建一個(gè)任務(wù)調(diào)度框架。這個(gè)示例還使用了Struts Action framework 插件,以便在web應(yīng)用啟動(dòng)時(shí)初始化任務(wù)調(diào)度機(jī)制。Struts是最常見(jiàn)的MVC框架,為大多數(shù)開(kāi)發(fā)人員所熟悉。當(dāng)然除此之外還有許多框架可以協(xié)助在web應(yīng)用中實(shí)現(xiàn)MVC模式。

          啟動(dòng)時(shí)初始化任務(wù)調(diào)度器

          我們首先要做的是建立一個(gè)Struts插件,讓它在容器啟動(dòng)時(shí)創(chuàng)建我們的任務(wù)調(diào)度器。在以下例子中,我們選擇Tomcat作為web應(yīng)用容器,不過(guò)這些示例在其他容器中也應(yīng)當(dāng)可以運(yùn)行。我們要?jiǎng)?chuàng)建一個(gè)Struts插件類,并在struts-config.xml中加入幾行代碼以使之可以工作。

          這個(gè)插件有兩個(gè)可配置的初始化參數(shù):startOnLoad指定是否要在容器啟動(dòng)時(shí)立即啟動(dòng)任務(wù)調(diào)度器,而 startupDelay指定啟動(dòng)任務(wù)調(diào)度器之前的等待時(shí)間。啟動(dòng)延時(shí)很有用,因?yàn)槲覀兛赡苄枰紫葓?zhí)行一些更重要的初始化步驟。此外還可以使用listener機(jī)制,以更復(fù)雜的方式來(lái)通知SchedulerPlugIn何時(shí)啟動(dòng)Quartz Scheduler。

          <PLUG-IN className="SchedulerPlugIn">

          <SET-PROPERTY property="startOnLoad" value="false" />

          <SET-PROPERTY property="startupDelay" value="0" />

          </PLUG-IN>

          我們要?jiǎng)?chuàng)建的是一個(gè)實(shí)現(xiàn)Struts插件接口org.apache.struts.action.PlugIn的單子類SchedulerPlugIn。Struts會(huì)按照配置文件中出現(xiàn)的順序初始化各個(gè)插件。要特別注意的是init()方法中的代碼,在此我們初始化了所需的Quartz對(duì)象,并得到Scheduler。我們的任務(wù)信息就要提交到此org.quartz.Scheduler對(duì)象,后者將在隨后討論。Scheduler對(duì)象由Quartz servlet根據(jù)其配置初始化,就像Struts初始化它的ActionServlet類一樣。讓我們來(lái)看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對(duì)象
          Scheduler scheduler = null;

          // Retrieve the factory from the ServletContext.
          // It will be put there by the Quartz Servlet
          // 從ServletContext取得由Quartz Servlet放置在此的factory對(duì)象。
          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尚未啟動(dòng),則啟動(dòng)它
          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.
          // 在啟動(dòng)之前等待指定長(zhǎng)度的時(shí)間
          Thread delayedScheduler =
          new Thread(new DelayedSchedulerStarted (
          scheduler, m_startupDelay));
          //give the scheduler a name. All good code needs a name
          //給任務(wù)調(diào)度器命名。好的代碼總該有名字!
          delayedScheduler.setName("Delayed_Scheduler");
          //Start out scheduler
          //啟動(dòng)任務(wù)調(diào)度器
          delayedScheduler.start();
          }
          } catch (Exception e){
          e.printStackTrace();
          }
          sm_scheduler = scheduler;
          }

          配置過(guò)程的第二步是在web.xml中加入用來(lái)初始化Quartz servlet(org.quartz.ee.servlet.QuartzInitializerServlet)的內(nèi)容,因?yàn)樾枰鼘chedulerFactory添加到ServletContext中,以便在我們的Struts插件中可以訪問(wèn)。SchedulerFactory就是我們?cè)赟truts插件中獲得Scheduler對(duì)象的來(lái)源。除了struts-config.xml 和web.xml之外,還要在web應(yīng)用的classes目錄下放置一個(gè)quartz.properties文件。此文件的位置也可以在web.xml中作為QuartzInitializerServlet的啟動(dòng)參數(shù)來(lái)指定。

          QuartzInitializer

          Quartz Initializer Servlet

          org.quartz.ee.servlet.QuartzInitializerServlet

          1

          shutdown-on-unload

          true

          start-scheduler-on-load

          false

          這里其實(shí)完全可以不使用Struts和SchedulerPlugIn,但如果將來(lái)決定要以其它的任務(wù)調(diào)度框架替換Quartz的話,額外的抽象層就很有用了。長(zhǎng)遠(yuǎn)看來(lái),讓一切保持松散耦合總會(huì)使工作變得容易些。如果你使用其它MVC框架,也可以用SchedulerPlugIn.init()方法中的代碼達(dá)到同樣的效果。此外,還可以用Servlet 2.3規(guī)范中的ServletContextListener來(lái)實(shí)現(xiàn)同樣的初始化過(guò)程。

          到此為止web應(yīng)用已配置完畢,我們可以創(chuàng)建一個(gè).war文件并部署到服務(wù)器上,從控制臺(tái)觀察SchedulerPlugIn的輸出信息。然而在此之前,讓我們先看看如何向任務(wù)調(diào)度器提交一項(xiàng)任務(wù)。

          我們可以從web應(yīng)用中的任何類訪問(wèn)SchedulerPlugIn的唯一實(shí)例,并調(diào)度一些要執(zhí)行的工作。首先需要一個(gè)Trigger(觸發(fā)器)對(duì)象來(lái)告訴任務(wù)何時(shí)運(yùn)行、每隔多久運(yùn)行一次。Quartz支持多種觸發(fā)器,在這個(gè)例子中我們使用CronTrigger。

          Trigger trigger = new CronTrigger("trigger1", "group1");

          trigger.setCronExpression("0 0 15 ? * WED");

          以上的觸發(fā)器會(huì)在每周三的下午3點(diǎn)執(zhí)行指定任務(wù)。現(xiàn)在我們只要?jiǎng)?chuàng)建一個(gè)JobDetail對(duì)象,并把它和上面的觸發(fā)器一起傳遞給SchedulerPlugIn的scheduleWork()方法。

          JobDetail jobDetail =
          new JobDetail("Hello World Job",
          "Hello World Group",
          HelloWorld.class,
          true, true, true);
          //Schedule The work
          //調(diào)度這項(xiàng)任務(wù)
          SchedulerPlugIn.scheduleWork(scheduledJobDetail, trigger);

          實(shí)際工作在何處?

          至此我們已決定Trigger,可以開(kāi)始調(diào)度工作了。看上去一切都已完成,但實(shí)際上我們只是調(diào)度了一項(xiàng)任務(wù),還有最重要的一步有待完成。注意HelloWorld.class作為參數(shù)傳遞給了JobDetail的構(gòu)造函數(shù)。這個(gè)類就是實(shí)際完成工作的地方。HelloWorld繼承了Quartz的Job類,并覆蓋了execute()方法。當(dāng)任務(wù)管理器決定運(yùn)行這個(gè)任務(wù)時(shí),execute()方法將被調(diào)用。來(lái)看代碼:

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

          //extend the proper Quartz class
          //繼承適當(dāng)?shù)腝uartz類
          public class HelloWorld extends Job {

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

          // Every job has it's own job detail
          //每個(gè)Job都有獨(dú)立的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
          //每個(gè)Job都有一個(gè)Job Data map來(lái)存放額外的信息
          JobDataMap dataMap = jobDetail.getJobDataMap();

          System.out.println("Hello World!!!");
          }
          }

          出于測(cè)試的目的,你可能希望將觸發(fā)器的頻率調(diào)的高一點(diǎn),以便觀察到HelloWorld的動(dòng)作。畢竟,你不想一直等到凌晨2點(diǎn)才能確定調(diào)度的任務(wù)確實(shí)運(yùn)行了。相反,你可能需要一個(gè)每隔10秒運(yùn)行的觸發(fā)器:

          Trigger trigger = new SimpleTrigger("trigger1", "group1");
          trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
          trigger.setRepeatInterval(10000L); // milliseconds毫秒

          注意,這個(gè)觸發(fā)器沒(méi)有使用類cron的語(yǔ)法。Quartz有大量各類的選項(xiàng)和配置方法,可適用于任何任務(wù)調(diào)度的需要。

          其它計(jì)時(shí)方式的配置

          Quartz提供了多種調(diào)度任務(wù)的方式。CronTrigger可能是最復(fù)雜的一種,不過(guò)還有其它的選擇。大多數(shù)觸發(fā)器可以由Quartz提供的TriggerUtils類創(chuàng)建。以下是一些常見(jiàn)的觸發(fā)器的例子。如諺語(yǔ)所言,條條大路通羅馬!

          每天凌晨2:22觸發(fā)的觸發(fā)器

          // 方法一:使用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秒執(zhí)行一次的觸發(fā)器

          /**
          * 方法一:makeSecondlyTrigger
          * 注意以下代碼將創(chuàng)建一個(gè)立即啟動(dòng)的觸發(fā)器。要控制啟動(dòng)時(shí)間,使用
          * trigger.setStartTime(Date)方法。
          */
          Trigger trigger = TriggerUtils.makeSecondlyTrigger(5);
          trigger.setName("MyFiveSecondTrigger");
          trigger.setGroup("MyTriggerGroup");
          /** 方法二:設(shè)置SimpleTrigger的重復(fù)次數(shù)和間隔時(shí)間。
          * 注意以下代碼將創(chuàng)建一個(gè)立即啟動(dòng)的觸發(fā)器。要控制啟動(dòng)時(shí)間,使用
          * trigger.setStartTime(Date)方法。
          */
          Trigger trigger = new SimpleTrigger("trigger1", "group1");
          trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
          trigger.setRepeatInterval(5000L);
          // milliseconds按間隔時(shí)間運(yùn)行任務(wù)
          Trigger trigger = new SimpleTrigger("trigger1", "group1");
          // 24 hours * 60(minutes per hour) *
          // 60(seconds per minute) * 1000(milliseconds per second)
          // 24小時(shí) * 60(分鐘每小時(shí)) * 60(秒每分鐘)* 1000(毫秒每秒鐘)
          trigger.setRepeatInterval(24L * 60L * 60L * 1000L);

          結(jié)論在這個(gè)演示中,我們只接觸了Quartz框架的一些初級(jí)功能。記住,Java 5 和J2EE 5也有自己的任務(wù)調(diào)度機(jī)制,但是它們不像Quartz那樣靈活易用。Quartz是目前唯一的開(kāi)源Java任務(wù)調(diào)度框架,它的確為開(kāi)發(fā)者的錦囊中增加了很有用的內(nèi)容。你可從Open Symphony下載Quartz,并得到一份很好的教程和使用說(shuō)明。

          jwebee

          我的個(gè)人網(wǎng)站
          posted on 2007-08-16 12:16 周行 閱讀(1124) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          Java-Android-jwebee
          主站蜘蛛池模板: 通海县| 本溪市| 竹山县| 开原市| 上栗县| 郎溪县| 维西| 澄迈县| 五常市| 龙川县| 南投市| 邮箱| 左权县| 青阳县| 新昌县| 汤原县| 青浦区| 昌平区| 五原县| 通道| 三穗县| 宜昌市| 嘉兴市| 和静县| 太白县| 定安县| 舒兰市| 静宁县| 新田县| 灵丘县| 大英县| 洞头县| 澄迈县| 乌兰察布市| 岢岚县| 建瓯市| 措勤县| 阜平县| 武宁县| 慈利县| 凤冈县|