Spring中的任務調度和線程池支持

1.Spring與Quartz

Quartz是Open Symphony開發的一款開源的任務調度框架。相對於JDK的Timer之類的簡單任務調度程序來說,Quartz擁有更爲全面的功能支持:

1)允許批處理任務狀態的持久化,並且提供不同的持久化策略支持

2)批處理任務的遠程調度

3)提供基於Web的監控接口

4)集羣支持

5)插件式的可擴展性

 

2.Quartz擁有明確的角色劃分,分別對應處理任務調度過程中的各種相關關注點:

1)Job:代表將要被調度的任務

2)JobDetail:主要職能是提供Job執行的上下文信息,Job所依賴的某些配置信息,可以通過JobDetail提供,二者通過JobDataMap進行數據交互

3)Trigger:用於觸發被調度任務的執行,可以根據不同的觸發條件,提供不同的Trigger實現

4)Scheduler:Quartz的核心調度程序,負責管理所有相關的Trgger和JobDetail

 

3.一個Trigger只能用於一個Job,但多個Trigger可以用於同一Job

Quartz中最經常使用的主要的兩種Trigger實現類:

一個是SimpleTrigger,可以指定簡單的基於時間間隔的調度規則

一個是CronTrigger,可以類似於Unix/Linux操作系統中Cron程序多使用的表達式來指定調度規則

 

public class FXNewsJob implements Job{

    private FXNewsProvider fxNewsProvider;

    //getXXX();

    //setXXX();

    public void execute(JobExecutionContext jobExecCtx) throws JobExecutionException{

          getFxNewsProvider().getAndPersistNews();

    }

}

 

Trigger simpleTrigger = new SimpleTrigger("triggerName",Scheduler.DEFAULT_GROUP,new Date(),null,SimpleTrigger.REPEAT_INDEFINITELY,60000);

Trigger cronTrigger = new CronTrigger("cronTriggerName",Scheduler.DEFAULT_GROUP,"0 0/1 * * * ?");

 

在Job和Trigger都具備後,通過Scheduler將它們關聯起來並進行最終的任務調度

Scheduler scheduler = new StdSchedulerFactory().getScheduler();

scheduler.start();

JobDetail jobDetail = new JobDetail("jobName",Scheduler.DEFAULT_GROUP,FXNewsJob.class);

Trigger cronTrigger = new CronTrigger("cronTriggerName",Scheduler.DEFAULT_GROUP,"0 0/1 * * * ?");

scheduler.scheduleJob(jobDetail,cronTrigger);

 

4.融入Spring大家庭的Quartz

1)Job的實現策略

在Quartz中,每一Job所需要的執行上下文(Execution Context)信息是由其對應的JobDetail提供的,二者通過JobDataMap進行數據通信

JobDetail jobDetail = new JobDetail("jobName",Scheduler.DEFAULT_GROUP,HelloWorldJob.class);

jobDetail.getJobDataMap().put("message","helloworld");

jobDetail.getJobDataMap().put("counter",10);

public class HelloWorldJob implements Job{

      public void execute(JobExecutionContext ctx) throws JobExecutionException{

            JobDataMap jobDataMap = ctx.getJobDetail().getJobDataMap();

            String message = jobDataMap.getString("message");

            int counter = jobDataMap.getInt("counter");

            //...

      }

}

Spring提供了org.springframework.scheduling.quartz.QuartzJobBean。在實現Job的時候,通過繼承QuartzJobBean,讓我們在Job實現類中,直接以bean屬性的形式訪問當前Job執行上下文信息

public class HelloWorldJobExtendingQuartzJobBean extends QuartzJobBean{

       private String message;

       private int counter;

       //getXXX();

       //setXXX();

       public void executeInternal(JobExecutionContext ctx) throws JobExecutionException{

             //getMessage()並使用

             //getCounter()並使用

       }

}

Spring提供了org.springframework.scheduling.quartz.SpringBeanJobFactory是一種更好的選擇,允許對要執行的Job進行定製

2)JobDetail的更多選擇

通過org.springframework.scheduling.quartz.JobDetailBean來創建和配置相應Job所對應的JobDetail實例

<bean>

       <property name="jobClass" value="HelloWorldJob | HelloWorldJobExtendingQuartzJobBean" />

       <property name="jobDataAsMap">

              <map>

                      <entry key="message">

                             <value>HelloWorld</value>

                      </entry>

                      <entry key="counter">

                             <value>10</value>

                      </entry>

              </map>

       </property>

</bean>

一個比較優雅的MethodInvokingJobDetailFactoryBean

<bean>

       <property name="targetObject" ref="fxNewsProvider" />

       <property name="targetMethod" value="getAndPersistNews" />

</bean>

MethodInvokingJobDetailFactoryBean會在內部構建相應的Job實現類(MethodInvokingJob和StatefulMethodInvokingJob),這些Job實現類會在合適的時機調用指定給MethodInvokingJobDetailFactoryBean的業務對象上的業務方法。但是,通過它所返回的JobDetail信息是不可序列化的,從而也就無法保存或者說持久化。

3)Trigger的可配置化

Spring分別提供了SimpleTriggerBean和CronTrggerBean封裝類,同樣採用FactoryBean機制實現

<bean>

       <property name="jobDetail" ref="jobDetail"/>

       <property name="repeatInterval" value="3000"/>

</bean>

<bean>

        <property name="jobDetail" ref="jobDetail"/>

        <property name="cronExpression" value="0 0/1 * * * ?"/>

</bean>

可以結合Trgger與Quartz提供的Calender來達成最終所需要的調度規則。Trigger負責提供覆蓋範圍足夠廣的調度規則,而Calender負責排除這一規則範圍內不需要的部分

4)Scheduler的新家

Spring提供了org.springframework.scheduling.quartz.SchedulerFactoryBean對Quartz的Scheduler進行管理

<bean>

         <property name="triggers">

                 <ref bean="newsTrigger" />

                 <ref bean="simpleTrigger" />

         </property>

</bean>

 

FXNewsProvider相關任務調度類配置代碼示例

<bean p:newsListener-ref="newsListener" p:newsListener-ref="newsPersister"/>

 

<bean>

         <property name="targetObject" ref="fxNewsProvider" />

         <property name="targetMethod" value="getAndPersistNews" />

</bean>

 

<bean>

         <property name="jobDetail" ref="jobDetail" />

         <property name="cronExpression" value="0 0/1 * * * ?" />

</bean>

 

<bean>

         <property name="triggers">

                  <list>

                         <ref bean="newsTrigger"/>

                  </list>

         </property>

</bean>

 

5.Spring對JDK Timer的集成

JDK Timer小記:所有經由Timer進行調度的任務都需要繼承TimerTask

public class FXNewsTimerTask extends TimerTask{

        private FXNewsProvider fxNewsProvider;

        //getXXX();

        //setXXX();

        public void run(){

              getFxNewsProvider().getAndPersistNews();

        }

}

FXNewsTimerTask task = new FXNewsTimerTask();

Timer timer = new Timer("schedulerName[optional]");

timer.schedule(task,0,60000);

1)逃離TimerTask的魔咒

<bean p:newListener-ref="newsListener" p:newPersistencer-ref="newsPersister">

<bean>

        <property name="targetObject" ref="fxNewsProvider"/>

        <property name="targetMethod" value="getAndPersistNews"/>

</bean>

2)TimerTask的模塊化封裝-ScheduledTimerTask

通過一個ScheduledTimerTask將對應的TimerTask和相關的Trigger信息封裝到一個可以統一管理的實體中

<bean>

         <property name="timerTask" ref="task" />

         <property name="period" value="3000">

</bean>

3)Timer的新家-TimerFactoryBean

TimerFactoryBean實現代碼摘錄

ScheduledTimerTask[] stt = ... ;

for(ScheduledTimerTask task : stt){

        if(task.isOneTimeTask()){

               timer.schedule(task.getTimerTask(),task.getDelay());

        }else{

               if(task.isFixedRate()){

                      timer.scheduleAtFixedRate(task.getTimerTask(),task.getDelay(),task.getPeriod());

               }else{

                      timer.schedule(task.getTimerTask(),task.getDelay(),task.getPeriod());

               }

        }

}

<bean>

        <property name="scheduledTimerTasks">

               <list>

                       <ref bean="scheduledTask" />

               </list>

        </property>

</bean>

 

6.Executor的孿生兄弟TaskExecutor

Java5爲我們帶來了一套新的任務執行框架,以Executor爲首:

public interface Executor{

      void execute(Runnable task);

}

Executor這一抽象的意義在於,可以將任務的提交(task commission)和任務的執行(task execution)策略分隔開來,解除二者之間的耦合性,以Runnable類型界定的任務提交之後,最終會以什麼樣的策略執行(什麼時間執行,交給誰執行,等等),完全由不同的Executor實現類負責,提交任務的客戶端完全可以忽略後者的實現細節

Spring提供的TaskExecutor完成Executor同樣的功能

public interface TaskExecutor{

      void execute(Runnable task);

}

 

7.可用的TaskExecutor

1)SyncTaskExecutor

提交給SyncTaskExecutor的任務將直接在當前線程中執行

public class SyncTaskExecutor implements TaskExecutor,Serializable{

      public void execute(Runnable task){

            Assert.notNull(task,"Runnable must not be null");

            task.run();

      }

}

2)SimpleAsyncTaskExecutor

提供最基本的異步執行能力,實現的方式則是爲每個任務都創建新的線程

public class SimpleAsyncTaskExecutor implements TaskExecutor{

      public void execute(Runnable task){

            new Thread(task).start();

      }

}

SimpleAsyncTaskExecutor提供了相應的屬性以控制創建的線程數目上限

<bean>

       <property name="concurrencyLimit" value="100" />

</bean>

3)ThreadPoolTaskExecutor

用線程池來管理並重用處理任務異步執行的工作線程,ThreadPoolTaskExecutor是對標準的java.util.concurrent.ThreadPoolExecutor進行了封裝

Spring提供的ThreadPoolTaskExecutor一共有兩個:

org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor是對java.util.concurrent.ThreadPoolExecutor進行了封裝

org.springframework.scheduling.backportconcurrent.ThreadPoolTaskExecutor是對JSR-166 backport的ThreadPoolTaskExecutor進行封裝

<bean>

        <property name="corePoolSize" value="10">

        <property name="maxPoolSize" value="20">

        <property name="queueCapacity" value="20">

</bean>

4)ConcurrentTaskExecutor

爲Java5的Executor和Spring的TaskExecutor搭建了一道橋樑。構建自己需要的Executor實例,通過Executor.newXXXThreadPool(),然後以ConcurrentTaskExecutor對其進行封裝,封裝後獲得的ConcurrentTaskExecutor即獲得響應Executor的能力。

Executor executor = Executors.newScheduledThreadPool(10);

TaskExecutor taskExecutor = new ConcurrentTaskExecutor(executor);

5)TimeTaskExecutor、SimpleThreadPoolTaskExecutor和WorkManagerTaskExecutor

都是使用特定的調度程序(Job Scheduler)來執行提交給他們的任務

public class PrototypeTimerTaskExecutor implements TaskExecutor{

       private Timer timer = new Timer();

       public void execute(final Runnable task){

             timer.schedule(new TimerTask(){ public void run(){task.run();}},new Date());

       }

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章