隨筆-72  評論-20  文章-0  trackbacks-1

          1、 概述

          在很多企業(yè)應(yīng)用中有時需要在特定的時間運(yùn)行一段代碼,比如銀行需要在晚上系統(tǒng)相對空閑的時間內(nèi)進(jìn)行日結(jié)的對帳,那么到了規(guī)定時間系統(tǒng)需要觸發(fā)對帳服 務(wù)運(yùn)行對帳程序,現(xiàn)在EJB定時器服務(wù)能解決這個問題,它是一個可靠的、事務(wù)性的、提供容器的服務(wù),允許Bean提供者注冊定時反饋的企業(yè)Beans,它 可以在特定時刻發(fā)生,或在某段時間之后發(fā)生,或以一定時間間隔重復(fù)發(fā)生。由于這個服務(wù)是可靠的,容器破壞的時候定時依然有效,企業(yè)Beans的激活與失 效、裝載與保存周期都由定時器注冊。定時器服務(wù)由EJB容器實(shí)現(xiàn),定時器服務(wù)可以通過EJBContext接口新增的getTimerService() 方法來訪問,它返回實(shí)現(xiàn)TimerService接口的對象:這個接口允許創(chuàng)建不同的定時器來支持在不同時間、不同時間間隔、不同時間周期時發(fā)生的定時反 饋。使用定時器服務(wù)的企業(yè)Beans的Bean類必須實(shí)現(xiàn)javax.ejb.TimedObject接口。在EJB2.1中,只有無序的會話Beans 和實(shí)體Beans可以注冊為定時器服務(wù)。這個功能在以后的規(guī)范中可能會擴(kuò)展到其它類型的Bean。

          定時器服務(wù)適合長時間的業(yè)務(wù)處理模型,但并不適合用于實(shí)時的事件模型。

          在 WebSphere Application Server 6中,EJB 定時服務(wù)將 EJB 計(jì)時器作為新的調(diào)度程序服務(wù)任務(wù)實(shí)施。缺省情況下,內(nèi)部調(diào)度程序?qū)嵗糜诠芾砟切┤蝿?wù),定時任務(wù)存放在與服務(wù)器進(jìn)程關(guān)聯(lián)的 Cloudscape 數(shù)據(jù)庫中。在集群環(huán)境中,任務(wù)必須存放在企業(yè)關(guān)系型數(shù)據(jù)庫中。下面我們以DB2為例講述怎樣在集群環(huán)境中配置定時服務(wù)。







          2、 創(chuàng)建用于定時服務(wù)的數(shù)據(jù)庫

          每個定時服務(wù)程序都需要一個數(shù)據(jù)庫,以用于存儲它的持久信息。數(shù)據(jù)庫及其位置應(yīng)當(dāng)由應(yīng)用程序開發(fā)者和服務(wù)器管理員決定。 定時服務(wù)程序使用這個數(shù)據(jù)庫來存儲任務(wù),然后運(yùn)行這些任務(wù)。定時服務(wù)程序性能極大地依賴于數(shù)據(jù)庫的性能。如果需要每秒執(zhí)行更多任務(wù),您可以在更大型的系統(tǒng) 中運(yùn)行定時服務(wù)程序守護(hù)程序,或通過使用多個定時服務(wù)程序?qū)θ蝿?wù)或分區(qū)使用的會話 bean 使用集群。但是,定時服務(wù)程序數(shù)據(jù)庫最終會達(dá)到飽和狀態(tài),此時您就需要一個更大型或更優(yōu)異的數(shù)據(jù)庫系統(tǒng)。要獲取有關(guān)調(diào)度程序拓?fù)涞脑敿?xì)信息,請參閱技術(shù)資 料"WebSphere Enterprise Scheduler planning and administration guide"。

          當(dāng)您在每個定時服務(wù)程序配置中指定唯一的表前綴值時,多個定時服務(wù)程序可以共享一個數(shù)據(jù)庫。這一共享可以降低定時服務(wù)程序數(shù)據(jù)庫的管理成本。

          TIPS:Oracle XA 數(shù)據(jù)庫的限制,Oracle XA 不允許在全局事務(wù)環(huán)境中執(zhí)行所需的模式操作。本地事務(wù)是不受支持的。如果您的調(diào)度程序使用 Oracle XA 數(shù)據(jù)源,您可以將調(diào)度程序配置臨時更改為使用一個非 XA Oracle 數(shù)據(jù)源,或者使用提供的 DDL 文件手工創(chuàng)建表。如果使用管理控制臺為配置為使用 Oracle XA 數(shù)據(jù)源的調(diào)度程序創(chuàng)建或刪除調(diào)度程序表,您將接收到一條 SchedulerDataStoreException 錯誤消息并且操作將失敗。

          下面我們將以DB2為例講述定時服務(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ī)使用了不兼容的代碼頁時,這將導(dǎo)致代碼頁轉(zhuǎn)換問題。要避免死鎖,請確保將 DB2 隔離級別設(shè)置為"讀穩(wěn)定性"。如果需要,請輸入命令 :


          db2set DB2_RR_TO_RS=YES

          然后重新啟動 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)已為定時服務(wù)創(chuàng)建了 DB2 數(shù)據(jù)庫。







          3、 創(chuàng)建定時服務(wù)的表空間和表

          在WAS6的安裝目錄下,有一個名為scheduler的目錄。下面包含WAS容器用來管理定時服務(wù)的各種數(shù)據(jù)庫SQL定義。 對應(yīng)于DB2的SQL定義文件名為createSchemaDB2.ddl和createTablespaceDB2.ddl,修改這兩個文件選擇你所要新建的表空間名和你所要的模式名稱。這兩個文件大致內(nèi)容如下:


          createTablespaceDB2.ddl
          CREATE TABLESPACE @SCHED_TABLESPACE@ MANAGED BY SYSTEM USING
          ( '@location@"@SCHED_TABLESPACE@' );

          可以修改表空間名稱,這個文件也可不做修改。然后修改createSchemaDB2.ddl 修改后的的結(jié)果可以去掉原來的模式名稱,那么新建用戶表的時候?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@";

          這兩個文件修改完成后,在命令行運(yùn)行db2cmd轉(zhuǎn)到db2命令窗口。

          然后運(yùn)行db2batch -d timerdb -f createTablespaceDB2.ddl

          和db2batch -d timerdb -f createSchemaDB2.ddl生成定時服務(wù)所需要的表空間和表。

          運(yùn)行完成后用下列命令驗(yàn)證:


          Db2 connect to timerdb
          Db2 list tables

          你將會看到有以下四個表被創(chuàng)建:


          Table/View    Schema      Type
          ------------ ------------- -----
          LMGR ADMIN T
          LMPR ADMIN T
          TASK ADMIN T
          TREG ADMIN T

          其中主表task存放了定時程序的相關(guān)信息。







          4、 創(chuàng)建新的集群

          分別在hosta,hostb,hostc上完成WAS6安裝后,我們需要創(chuàng)建3個節(jié)點(diǎn)來組成一個新的群集。

          1)在hosta上創(chuàng)建一個Network DeployManagement節(jié)點(diǎn),啟動概要表創(chuàng)建向?qū)В?/p>

          選擇創(chuàng)建Deployment Manager概要表:



          點(diǎn)下一步直至完成

          2)分別在hostc和hostb兩個節(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兩個應(yīng)用服務(wù)器節(jié)點(diǎn)上運(yùn)行addNode.sh hosta 8879

          6)運(yùn)行完成后,打開ND管理控制臺:http://hosta:9060/ibm/console

          7)在服務(wù)器下新建一個群集timertest,創(chuàng)建兩個成員為clus01,clus02。啟動群集。







          5、 創(chuàng)建定時服務(wù)的數(shù)據(jù)源

          進(jìn)入ND管理控制臺,展開資源,點(diǎn)擊JDBC 提供者,選擇要新建的資源所在的服務(wù)器



          點(diǎn)新建。按提示輸入所需資料。點(diǎn)數(shù)據(jù)源,進(jìn)入數(shù)據(jù)源頁面。

          新建一個名為testtimer的數(shù)據(jù)源,指定jndi名為jdbc/testtimer



          測試連接通過后。做下一步設(shè)置。







          6、 修改服務(wù)器設(shè)置

          打開管理控制臺。

          單擊服務(wù)器 >應(yīng)用程序服務(wù)器 > 服務(wù)器名 > EJB 容器設(shè)置 > EJB 定時服務(wù)設(shè)置。 出現(xiàn)"定時服務(wù)設(shè)置"面板。

          如果您要使用內(nèi)部或預(yù)配置的調(diào)度程序?qū)嵗瑒t單擊使用內(nèi)部 EJB 定時服務(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ù)點(diǎn).。具體對應(yīng)的每個值的意思可以點(diǎn)擊幫助頁面查看。







          7、 開發(fā)基于J2EE標(biāo)準(zhǔn)的定時服務(wù)企業(yè)bean

          下面的例子是在RAD環(huán)境下開發(fā),要實(shí)現(xiàn)定時服務(wù),EJB必須要實(shí)現(xiàn)javax.ejb.TimedObject接口。

          EJB里面需要涉及到的接口或類分別是:

          要實(shí)現(xiàn)的TimedObject和TimerService,Timer。

          在EJB中的ejbContext增加了一個方法.getTimerService();用于獲得TimerService類。但這個方法不能在 setEntityContext()、setSessionContext()、setMessageContext()方法中調(diào)用。

          下面列出了三個接口的定義:


          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()中設(shè)置Timer。主要是因?yàn)閑jbCreate和ejbRemove調(diào)用的時間和次數(shù)都因Container Vendor而異。可能導(dǎo)致錯誤設(shè)置Timer。

          對MessageDriven Bean 而言,和Stateless SessionBean的情況基本相似。但是設(shè)置Timer應(yīng)該在onMessage()里面。通過一個JMS來進(jìn)行觸發(fā)。

          ejbTimedout是一個回調(diào)方法,執(zhí)行具體的商業(yè)邏輯,那么怎樣設(shè)置什么時間觸發(fā)這個方法呢,我們利用javax.ejb.TimerSevice。該對象我們可以從EJBContext中獲得該對象實(shí)例。

          當(dāng) 定時器創(chuàng)建的特定時間到達(dá)后,容器就會觸發(fā)ejbTimeout(),運(yùn)行ejbTimeOut方法提,Bean在終止之前通過調(diào)用定時器Cancel方 法取消定時器,它是定時器接口的一部分,如果定時器被取消,ejbTimeout()方法就不會被調(diào)用了。定時器接口的getHandle()方法返回一 個序列化的handle對象。接下來,這個持續(xù)的Handle能夠"非序列化",通過調(diào)用getTimer()方法得到定時器。由于定時器是本地對象, TimerHandle不必通過Bean的遠(yuǎn)程接口或Web Services接口來傳遞。

          具體步驟如下:

          新建一個EJB項(xiàng)目otherTimer,在這個EJB項(xiàng)目里新建一個otherTimer的會話Bean。

          在Bean實(shí)體里面需要實(shí)現(xiàn)兩個方法:

          startTimer()和ejbTimeOut。

          在startTimer()方法里面,我們通過EJBCONTEXT取得一個TimerService然后創(chuàng)建一個Timer。這個timer將在 2005,9月19日晚上8點(diǎn)過5分觸發(fā),觸發(fā)后,EJB容器會調(diào)用ejbTimeOut()方法運(yùn)行具體的商業(yè)邏輯,并且這個Timer會在80000 毫秒后再次觸發(fā)。


          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實(shí)現(xiàn)

          基于WAS Scheduler實(shí)現(xiàn)定時服務(wù),需要配置一個scheduler,在WAS管理控制臺展開資源,點(diǎn)scheduler,新建一個scheduler指定 名稱、JNDI名、數(shù)據(jù)源JNDI名(這里可以用前面設(shè)置的jndi/testtimer)和表前綴,跟前面設(shè)置服務(wù)器EJB定時服務(wù)容器設(shè)置類似。

          設(shè)置完成后就可以使用scheduler來實(shí)現(xiàn)定時服務(wù)了。

          在EJB模塊中創(chuàng)建一個無狀態(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ù)時,將調(diào)用 process() 方法。Home 和 Remote 接口在部署描述符 bean 中必須設(shè)置如下:


          com.ibm.websphere.scheduler.TaskHandlerHome 
          com.ibm.websphere.scheduler.TaskHandler

          通過使用以下示例工廠方法創(chuàng)建 BeanTaskInfo 接口的一個實(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)建一個占位符。直到調(diào)用調(diào)度程序中的 create() 方法,才會將任務(wù)添加到持久存儲中。設(shè)置 BeanTaskInfo 對象中的參數(shù)。 這些參數(shù)定義了調(diào)用哪些會話 bean 以及何時調(diào)用它們。TaskInfo 接口包含可用于控制任務(wù)執(zhí)行的各種 set() 方法,其中包括運(yùn)行任務(wù)的時間以及運(yùn)行任務(wù)時它執(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)前時間的30000毫秒后觸發(fā)process方法,在taskinfo里面可以設(shè)置一些其他schduler的屬性,比如運(yùn)行次數(shù),運(yùn)行間隔等。

          文章里涉及的J2EE標(biāo)準(zhǔn)的定時服務(wù)程序在附件的testTimer.ear里面,使用WAS Scheduler的程序在附件的AccountReport.ear里。其中AccountReport.ear是使用WAS自帶的Sample程序修改后的程序。

          posted on 2007-11-02 11:16 前方的路 閱讀(1035) 評論(0)  編輯  收藏 所屬分類: Web應(yīng)用服務(wù)器軟件思想
          主站蜘蛛池模板: 怀来县| 津市市| 陈巴尔虎旗| 焦作市| 临湘市| 芦溪县| 迁西县| 旬阳县| 闽侯县| 织金县| 香格里拉县| 株洲县| 诸城市| 崇仁县| 宿迁市| 济阳县| 德州市| 铁岭市| 万荣县| 武鸣县| 盐边县| 岑溪市| 宁德市| 新平| 泌阳县| 康平县| 林口县| 西丰县| 外汇| 青川县| 太湖县| 会宁县| 蓬溪县| 印江| 柳江县| 宁强县| 宁河县| 河南省| 兴义市| 南丹县| 墨玉县|