Java Web應用中的任務調度

爲何需要任務調度?


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的單子類SchedulerPlugInStruts會按照配置文件中出現的順序初始化各個插件。要特別注意的是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 servletorg.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>

這裏其實完全可以不使用StrutsSchedulerPlugIn,但如果將來決定要以其它的任務調度框架替換Quartz的話,額外的抽象層就很有用了。長遠看來,讓一切保持鬆散耦合總會使工作變得容易些。如果你使用其它MVC框架,也可以用SchedulerPlugIn.init()方法中的代碼達到同樣的效果。此外,還可以用Servlet 2.3規範中的ServletContextListener來實現同樣的初始化過程。

此爲止web應用已配置完畢,我們可以創建一個.war文件並部署到服務器上,從控制檯觀察SchedulerPlugIn的輸出信息。然而在此之前,讓我們先看看如何向任務調度器提交一項任務。未完待續接着下篇博客或者下載文檔http://wenku.it168.com/d_000068380.shtml

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