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