1、 概述
在很多企業應用中有時需要在特定的時間運行一段代碼,比如銀行需要在晚上系統相對空閑的時間內進行日結的對帳,那么到了規定時間系統需要觸發對帳服
務運行對帳程序,現在EJB定時器服務能解決這個問題,它是一個可靠的、事務性的、提供容器的服務,允許Bean提供者注冊定時反饋的企業Beans,它
可以在特定時刻發生,或在某段時間之后發生,或以一定時間間隔重復發生。由于這個服務是可靠的,容器破壞的時候定時依然有效,企業Beans的激活與失
效、裝載與保存周期都由定時器注冊。定時器服務由EJB容器實現,定時器服務可以通過EJBContext接口新增的getTimerService()
方法來訪問,它返回實現TimerService接口的對象:這個接口允許創建不同的定時器來支持在不同時間、不同時間間隔、不同時間周期時發生的定時反
饋。使用定時器服務的企業Beans的Bean類必須實現javax.ejb.TimedObject接口。在EJB2.1中,只有無序的會話Beans
和實體Beans可以注冊為定時器服務。這個功能在以后的規范中可能會擴展到其它類型的Bean。
定時器服務適合長時間的業務處理模型,但并不適合用于實時的事件模型。
在 WebSphere Application Server 6中,EJB 定時服務將 EJB
計時器作為新的調度程序服務任務實施。缺省情況下,內部調度程序實例用于管理那些任務,定時任務存放在與服務器進程關聯的 Cloudscape
數據庫中。在集群環境中,任務必須存放在企業關系型數據庫中。下面我們以DB2為例講述怎樣在集群環境中配置定時服務。
2、 創建用于定時服務的數據庫
每個定時服務程序都需要一個數據庫,以用于存儲它的持久信息。數據庫及其位置應當由應用程序開發者和服務器管理員決定。
定時服務程序使用這個數據庫來存儲任務,然后運行這些任務。定時服務程序性能極大地依賴于數據庫的性能。如果需要每秒執行更多任務,您可以在更大型的系統
中運行定時服務程序守護程序,或通過使用多個定時服務程序對任務或分區使用的會話 bean
使用集群。但是,定時服務程序數據庫最終會達到飽和狀態,此時您就需要一個更大型或更優異的數據庫系統。要獲取有關調度程序拓撲的詳細信息,請參閱技術資
料"WebSphere Enterprise Scheduler planning and administration guide"。
當您在每個定時服務程序配置中指定唯一的表前綴值時,多個定時服務程序可以共享一個數據庫。這一共享可以降低定時服務程序數據庫的管理成本。
TIPS:Oracle XA 數據庫的限制,Oracle XA
不允許在全局事務環境中執行所需的模式操作。本地事務是不受支持的。如果您的調度程序使用 Oracle XA
數據源,您可以將調度程序配置臨時更改為使用一個非 XA Oracle 數據源,或者使用提供的 DDL
文件手工創建表。如果使用管理控制臺為配置為使用 Oracle XA 數據源的調度程序創建或刪除調度程序表,您將接收到一條
SchedulerDataStoreException 錯誤消息并且操作將失敗。
下面我們將以DB2為例講述定時服務:
在機器hostdb上安裝DB2后,打開 DB2 命令行窗口。
確保您擁有數據庫系統的管理員權限,驗證此數據庫確實支持 Unicode(UTF-8)。 否則,此數據庫無法存儲 Java
代碼中可以處理的所有字符,當客戶機使用了不兼容的代碼頁時,這將導致代碼頁轉換問題。要避免死鎖,請確保將 DB2
隔離級別設置為"讀穩定性"。如果需要,請輸入命令 :
然后重新啟動 DB2 實例以激活這一更改。在 DB2 命令行處理程序中輸入以下命令使用示例名 timerdb 創建數據庫:
db2 CREATE DATABASE scheddb USING CODESET UTF-8 TERRITORY en-us
|
即可創建名為 timerdb 的 DB2 數據庫。
現已為定時服務創建了 DB2 數據庫。
3、 創建定時服務的表空間和表
在WAS6的安裝目錄下,有一個名為scheduler的目錄。下面包含WAS容器用來管理定時服務的各種數據庫SQL定義。
對應于DB2的SQL定義文件名為createSchemaDB2.ddl和createTablespaceDB2.ddl,修改這兩個文件選擇你所要新建的表空間名和你所要的模式名稱。這兩個文件大致內容如下:
createTablespaceDB2.ddl
CREATE TABLESPACE @SCHED_TABLESPACE@ MANAGED BY SYSTEM USING
( '@location@"@SCHED_TABLESPACE@' );
|
可以修改表空間名稱,這個文件也可不做修改。然后修改createSchemaDB2.ddl
修改后的的結果可以去掉原來的模式名稱,那么新建用戶表的時候將使用缺省連接數據庫的用戶的模式名。
CREATE TABLE "TASK" ("TASKID" BIGINT NOT NULL ,
"VERSION" VARCHAR(5) NOT NULL ,
"ROW_VERSION" INTEGER NOT NULL ,
"TASKTYPE" INTEGER NOT NULL ,
"TASKSUSPENDED" SMALLINT NOT NULL ,
"CANCELLED" SMALLINT NOT NULL ,
"NEXTFIRETIME" BIGINT NOT NULL ,
"STARTBYINTERVAL" VARCHAR(254) ,
"STARTBYTIME" BIGINT ,
"VALIDFROMTIME" BIGINT ,
"VALIDTOTIME" BIGINT ,
"REPEATINTERVAL" VARCHAR(254) ,
"MAXREPEATS" INTEGER NOT NULL ,
"REPEATSLEFT" INTEGER NOT NULL ,
"TASKINFO" BLOB(102400) LOGGED NOT COMPACT ,
"NAME" VARCHAR(254) NOT NULL ,
"AUTOPURGE" INTEGER NOT NULL ,
"FAILUREACTION" INTEGER ,
"MAXATTEMPTS" INTEGER ,
"QOS" INTEGER ,
"PARTITIONID" INTEGER ,
"OWNERTOKEN" VARCHAR(200) NOT NULL ,
"CREATETIME" BIGINT NOT NULL ) IN "@SCHED_TABLESPACE@";
|
這兩個文件修改完成后,在命令行運行db2cmd轉到db2命令窗口。
然后運行db2batch -d timerdb -f createTablespaceDB2.ddl
和db2batch -d timerdb -f createSchemaDB2.ddl生成定時服務所需要的表空間和表。
運行完成后用下列命令驗證:
Db2 connect to timerdb
Db2 list tables
|
你將會看到有以下四個表被創建:
Table/View Schema Type
------------ ------------- -----
LMGR ADMIN T
LMPR ADMIN T
TASK ADMIN T
TREG ADMIN T
|
其中主表task存放了定時程序的相關信息。
4、 創建新的集群
分別在hosta,hostb,hostc上完成WAS6安裝后,我們需要創建3個節點來組成一個新的群集。
1)在hosta上創建一個Network DeployManagement節點,啟動概要表創建向導:
選擇創建Deployment Manager概要表:
點下一步直至完成
。
2)分別在hostc和hostb兩個節點上選擇創建應用服務器概要表。
3)創建完成后在DeployManager概要上運行startManager.sh啟動Network Manager。
4)啟動完成后打開概要下的日志文件SystemOut.log查看soap端口,缺省為8879。
5)在hostb和hostc兩個應用服務器節點上運行addNode.sh hosta 8879
6)運行完成后,打開ND管理控制臺:http://hosta:9060/ibm/console
7)在服務器下新建一個群集timertest,創建兩個成員為clus01,clus02。啟動群集。
5、 創建定時服務的數據源
進入ND管理控制臺,展開資源,點擊JDBC 提供者,選擇要新建的資源所在的服務器
點新建。按提示輸入所需資料。點數據源,進入數據源頁面。
新建一個名為testtimer的數據源,指定jndi名為jdbc/testtimer
測試連接通過后。做下一步設置。
6、 修改服務器設置
打開管理控制臺。
單擊服務器 >應用程序服務器 > 服務器名 > EJB 容器設置 > EJB 定時服務設置。 出現"定時服務設置"面板。
如果您要使用內部或預配置的調度程序實例,則單擊使用內部 EJB 定時服務調度程序實例單選按鈕。
如果您選擇不更改缺省的設置,則此實例與 Cloudscape 數據庫相關聯。
更改數據源選擇輸入您所選的數據源別名。選擇前面創建的jdbc/testtimer數據源。
輸入表前綴為你創建表時的用戶缺省模式名稱,必須注意的是,在模式名稱后面必須要帶上一個小數點.。具體對應的每個值的意思可以點擊幫助頁面查看。
7、 開發基于J2EE標準的定時服務企業bean
下面的例子是在RAD環境下開發,要實現定時服務,EJB必須要實現javax.ejb.TimedObject接口。
EJB里面需要涉及到的接口或類分別是:
要實現的TimedObject和TimerService,Timer。
在EJB中的ejbContext增加了一個方法.getTimerService();用于獲得TimerService類。但這個方法不能在
setEntityContext()、setSessionContext()、setMessageContext()方法中調用。
下面列出了三個接口的定義:
public interface TimedObject{
public void ejbTimeout(Timer timer);
}
public interface TimerService{
public Timer createTimer(Date expiration,Serializable info);
public Timer createTimer(long duration,Serialzable info);
public Timer createTimer(Date initalExpiration,long intervalDuration,Serializable info);
public Timer createTimer(long initalDuration,long intevalDuration,Serializable info);
public java.util.Collection getTimers();
}
public interface Timer{
public void cancel();
public java.io.Serializable getInfo();
public Date getNextTimeout();
public long getTimeRemaining();
public TimerHandle getHandle();//這是一個local對象,不能傳到remote client端使用
}
|
對于Stateless SessionBean來說,不要在ejbCreate()和ejbRemove()中設置Timer。主要是因為ejbCreate和ejbRemove調用的時間和次數都因Container Vendor而異。可能導致錯誤設置Timer。
對MessageDriven Bean 而言,和Stateless SessionBean的情況基本相似。但是設置Timer應該在onMessage()里面。通過一個JMS來進行觸發。
ejbTimedout是一個回調方法,執行具體的商業邏輯,那么怎樣設置什么時間觸發這個方法呢,我們利用javax.ejb.TimerSevice。該對象我們可以從EJBContext中獲得該對象實例。
當
定時器創建的特定時間到達后,容器就會觸發ejbTimeout(),運行ejbTimeOut方法提,Bean在終止之前通過調用定時器Cancel方
法取消定時器,它是定時器接口的一部分,如果定時器被取消,ejbTimeout()方法就不會被調用了。定時器接口的getHandle()方法返回一
個序列化的handle對象。接下來,這個持續的Handle能夠"非序列化",通過調用getTimer()方法得到定時器。由于定時器是本地對象,
TimerHandle不必通過Bean的遠程接口或Web Services接口來傳遞。
具體步驟如下:
新建一個EJB項目otherTimer,在這個EJB項目里新建一個otherTimer的會話Bean。
在Bean實體里面需要實現兩個方法:
startTimer()和ejbTimeOut。
在startTimer()方法里面,我們通過EJBCONTEXT取得一個TimerService然后創建一個Timer。這個timer將在
2005,9月19日晚上8點過5分觸發,觸發后,EJB容器會調用ejbTimeOut()方法運行具體的商業邏輯,并且這個Timer會在80000
毫秒后再次觸發。
javax.ejb.TimerService ts=this.getSessionContext().getTimerService();
System.out.println("啟動一個時鐘!");
Timer timer = ts.createTimer(new Date(105,9,19,20,5,0),80000,"other timer");
|
8、 WAS Scheduler實現
基于WAS
Scheduler實現定時服務,需要配置一個scheduler,在WAS管理控制臺展開資源,點scheduler,新建一個scheduler指定
名稱、JNDI名、數據源JNDI名(這里可以用前面設置的jndi/testtimer)和表前綴,跟前面設置服務器EJB定時服務容器設置類似。
設置完成后就可以使用scheduler來實現定時服務了。
在EJB模塊中創建一個無狀態會話bean,該 bean 實現了
com.ibm.websphere.scheduler.TaskHandler 遠程接口中的 process()
方法。將您要創建的業務邏輯放入 process() 方法中。當運行任務時,將調用 process() 方法。Home 和 Remote
接口在部署描述符 bean 中必須設置如下:
com.ibm.websphere.scheduler.TaskHandlerHome
com.ibm.websphere.scheduler.TaskHandler
|
通過使用以下示例工廠方法創建 BeanTaskInfo 接口的一個實例。 使用 JSP 文件、servlet 或 EJB 組件創建實例,如以下代碼示例所示。此代碼必須與先前創建的 TaskHandler EJB 模塊位于同一應用程序中:
Object schedulerObj = initialContext.lookup("java:comp/env/Scheduler");
BeanTaskInfo taskInfo = (BeanTaskInfo) schedulerObj.createTaskInfo(BeanTaskInfo.class)
|
注: 創建 BeanTaskInfo 對象并不會將任務添加到持久存儲中。它將為必要的數據創建一個占位符。直到調用調度程序中的
create() 方法,才會將任務添加到持久存儲中。設置 BeanTaskInfo 對象中的參數。 這些參數定義了調用哪些會話 bean
以及何時調用它們。TaskInfo 接口包含可用于控制任務執行的各種 set() 方法,其中包括運行任務的時間以及運行任務時它執行的操作。
BeanTaskInfo 接口要求使用 setTaskHandler 方法設置 TaskHandler JNDI 名稱或
TaskHandlerHome。如果使用 WASScheduler MBean API 來設置任務處理程序,則 JNDI 名稱必須是標準的全局
JNDI 名稱。
使用 TaskInfo 接口 API 方法設置參數,如以下代碼示例所示:
java.util.Date startDate = new java.util.Date(System.currentTimeMillis()+30000);
Object reportGenHomeObj = initialContext.lookup("java:comp/env/ejb/ReportGenerator");
TaskHandlerHome reportGenHome = (TaskHandlerHome)PortableRemoteObject.narrow
(reportGenHomeObj,TaskHandlerHome.class); taskInfo.setTaskHandler(home);
taskInfo.setStartTime(startDate);
scheduler.create(taskInfo);
|
那么EJB容器將在當前時間的30000毫秒后觸發process方法,在taskinfo里面可以設置一些其他schduler的屬性,比如運行次數,運行間隔等。
文章里涉及的J2EE標準的定時服務程序在附件的testTimer.ear里面,使用WAS Scheduler的程序在附件的AccountReport.ear里。其中AccountReport.ear是使用WAS自帶的Sample程序修改后的程序。
posted on 2007-11-02 11:16
前方的路 閱讀(1028)
評論(0) 編輯 收藏 所屬分類:
Web應用服務器 、
軟件思想