亚洲av色香蕉一区二区三区,十四以下岁毛片带血a级,亚洲 校园 欧美 国产 另类,亚洲av日韩av一区谷露,色欲av无码一区二区三区

您現(xiàn)在的位置: > 技術(shù)沙龍 > WEB開發(fā) > JSP > 集群與jetspeed
  • 相關(guān)軟件
    >集群與jetspeed 創(chuàng)建者:webmaster 更新時間:2005-06-12 00:00

    集群與jetspeed



     

       

    logo.gifwidth=120 align=right vspace=5
    border=0>本文目的在于分析Jetspeed支持集群的現(xiàn)狀。首先介紹了集群計算的背景知識,然后使用tomcat作為例子配置了一個集群,接著分析了jetspeed對集群的height=43 alt=apache_portals.gif hspace=5 src="/tea/image/section/13736.gif"
    width=100 align=left vspace=5
    border=0>支持現(xiàn)狀,提出了解決這些問題的辦法,最后詳細(xì)解釋了jetspeed保存sesson數(shù)據(jù)的操作,這將對jetspeed的改造有幫助。


     

     

    1 、 集群背景介紹


     

    1.1 術(shù)語定義

        服務(wù)軟體是b/s或c/s結(jié)構(gòu)的s部分,是為b或c提供服務(wù)的服務(wù)性軟件系統(tǒng)。


     

    服務(wù)硬體指提供計算服務(wù)的硬件、比如pc機(jī)、pc服務(wù)器。


     

    服務(wù)實體通指服務(wù)軟體和服務(wù)硬體。


     

    客戶端指接受服務(wù)實體服務(wù)的軟件或硬件。


     

    1.2 兩大關(guān)鍵特性

        集群是一組協(xié)同工作的服務(wù)實體,用以提供比單一服務(wù)實體更具擴(kuò)展性與可用性的服務(wù)平臺。在客戶端看來,一個集群就象是一個服務(wù)實體,但事實上集群由一組服務(wù)實體組成。與單一服務(wù)實體相比較,集群提供了以下兩個關(guān)鍵特性:


     

         
    • 可擴(kuò)展性--集群的性能不限于單一的服務(wù)實體,新的服務(wù)實體可以動態(tài)地加入到集群,從而增強(qiáng)集群的性能。
         
    • 高可用性--集群通過服務(wù)實體冗余使客戶端免于輕易遇到out of service的警告。在集群中,同樣的服務(wù)可以由多個服務(wù)實體提供。如果一個服務(wù)實體失敗了,另一個服務(wù)實體會接管失敗的服務(wù)實體。集群提供的從一個出錯的服務(wù)實體恢復(fù)到另一個服務(wù)實體的功能增強(qiáng)了應(yīng)用的可用性。

    •  

     

    1.3 兩大能力

        為了具有可擴(kuò)展性和高可用性特點,集群的必須具備以下兩大能力:


     

         
    • 負(fù)載均衡--負(fù)載均衡能把任務(wù)比較均衡地分布到集群環(huán)境下的計算和網(wǎng)絡(luò)資源。
         
    • 錯誤恢復(fù)--由于某種原因,執(zhí)行某個任務(wù)的資源出現(xiàn)故障,另一服務(wù)實體中執(zhí)行同一任務(wù)的資源接著完成任務(wù)。這種由于一個實體中的資源不能工作,另一個實體中的資源透明的繼續(xù)完成任務(wù)的過程叫錯誤恢復(fù)。

    •  

     

    負(fù)載均衡和錯誤恢復(fù)都要求各服務(wù)實體中有執(zhí)行同一任務(wù)的資源存在,而且對于同一任務(wù)的各個資源來說,執(zhí)行任務(wù)所需的信息視圖(信息上下文)必須是一樣的。


     

    1.4 兩大技術(shù)

        實現(xiàn)集群務(wù)必要有以下兩大技術(shù):


     

         
    • 集群地址--集群由多個服務(wù)實體組成,集群客戶端通過訪問集群的集群地址獲取集群內(nèi)部各服務(wù)實體的功能。具有單一集群地址(也叫單一影像)是集群的一個基本特征。維護(hù)集群地址的設(shè)置被稱為負(fù)載均衡器。負(fù)載均衡器內(nèi)部負(fù)責(zé)管理各個服務(wù)實體的加入和退出,外部負(fù)責(zé)集群地址向內(nèi)部服務(wù)實體地址的轉(zhuǎn)換。有的負(fù)載均衡器實現(xiàn)真正的負(fù)載均衡算法,有的只支持任務(wù)的轉(zhuǎn)換。只實現(xiàn)任務(wù)轉(zhuǎn)換的負(fù)載均衡器適用于支持ACTIVE-STANDBY的集群環(huán)境,在那里,集群中只有一個服務(wù)實體工作,當(dāng)正在工作的服務(wù)實體發(fā)生故障時,負(fù)載均衡器把后來的任務(wù)轉(zhuǎn)向另外一個服務(wù)實體。
         
    • 內(nèi)部通信--為了能協(xié)同工作、實現(xiàn)負(fù)載均衡和錯誤恢復(fù),集群各實體間必須時常通信,比如負(fù)載均衡器對服務(wù)實體心跳測試信息、服務(wù)實體間任務(wù)執(zhí)行上下文信息的通信。

    •  

     

    具有同一個集群地址使得客戶端能訪問集群提供的計算服務(wù),一個集群地址下隱藏了各個服務(wù)實體的內(nèi)部地址,使得客戶要求的計算服務(wù)能在各個服務(wù)實體之間分布。內(nèi)部通信是集群能正常運轉(zhuǎn)的基礎(chǔ),它使得集群具有均衡負(fù)載和錯誤恢復(fù)的能力。


     

    2 集群配置


     

    image003.gifsrc="/tea/image/section/13737.gif" width=462 align=baseline vspace=5
    border=0>


     

    從上圖可知,由服務(wù)實體1、服務(wù)實體2和負(fù)載均衡器組成了一個集群。服務(wù)實體1和服務(wù)實體2參與對客戶端的服務(wù)支持工作,均衡負(fù)載器為客戶端維護(hù)集群的單一影像。集群實體間通過內(nèi)部的通信網(wǎng)交流信息,這種交流機(jī)制一般采用組播協(xié)議。負(fù)載均衡器通過內(nèi)部通信網(wǎng)探測各服務(wù)實體的心跳信息,服務(wù)實體間通過內(nèi)部通信網(wǎng)完成任務(wù)資源的傳播。可以看出,配置集群主要由配置服務(wù)實體和配置負(fù)載均衡器兩部分組成。本文使用tomcat 4.12、apache 2.0.43配置集群環(huán)境,相關(guān)軟件的部署圖如下:


     

    image006.gifsrc="/tea/image/section/13738.gif" width=500 align=baseline vspace=5
    border=0>


     

    服務(wù)實體1/2,負(fù)載均衡器可以部署在不同的機(jī)器上,也可以在同一機(jī)器上,本文環(huán)境為同一機(jī)器。


     

    2.1 準(zhǔn)備軟件


     
     

    2.2 配置負(fù)載均衡器

        在apache下配置負(fù)載均衡器分為三步,注意每次修改httpd.conf和workers2.properties時不要忘了重新啟動apache。


     

         
    • 第一步,安裝和調(diào)試apache
           


           

      負(fù)載均衡器jk2模塊是apache www 服務(wù)的插件,所以配置負(fù)載均衡器就得先安裝apache。本文下載的是windows版本 2.0.43,執(zhí)行setup.exe并回答一些簡單問題就可完成apache的任務(wù)。值得注意的是,安裝并啟動apache后如果apache對http://localhost/ 地址沒反應(yīng),你得修改apache安裝路徑下htdocs目錄下的index.html.xx文件,比如把index.html.en改成index.html。


         
    • 第二步,安裝jk2
           

      把下載到的
      target=_blank>mod_jk2-2.0.43.dll
      改成mod_jk2.dll 放到apache的modules目錄下,修改apache的httpd.conf,即在LoadModule foo_module modules/mod_foo.so 行下插入mod_jk2模塊的裝載信息:
           


             
             
               
             
             
           


      # Example:

      # LoadModule foo_module modules/mod_foo.so

      #

      LoadModule jk2_module modules/mod_jk2.dll


           


         
    • 第三步,配置jk2
           

      jk2的配置全在一個配置文件中,文件名為workers2.properties,和apache 的httpd.conf放在同一個目錄下。以下是這個文件的內(nèi)容:
           


             
             
               
             
             
           


      #++++++++++++++++++++++++++++++++++++

      # only at beginnin. In production uncomment it out

      [logger.apache2]

      level=DEBUG

      #shm必須配

      [shm]

      file=D:\Program Files\Apache Group\Apache2\logs\shm.file

      size=1048576


      # 第一個tomcat 的地址

      # Example socket channel, override port and host.

      [channel.socket:tomcat1]

      port=11009

      host=127.0.0.1

      # 定義第一個工作者指向第一個tomcat

      # define the worker

      [ajp13:tomcat1]

      channel=channel.socket:tomcat1


      #第二個tomcat 得地址

      # Example socket channel, override port and host.

      [channel.socket:tomcat2]

      port=12009

      host=10.1.36.123

      # 定義第二個工作者指向第二個tomcat

      # define the worker

      [ajp13:tomcat2]

      channel=channel.socket:tomcat2


      #定義負(fù)載均衡器,使其包含兩個工作者

      [lb:lb1]

      worker=ajp13:tomcat2

      worker=ajp13:tomcat1


      #指定負(fù)載均衡器完成單一地址映射,使得apache 服務(wù)所在的uri全部指向 兩個
      #tomcat 上的 root Uri mapping

      [uri:/*]

      group=lb:lb1

      #++++++++++++++++++++++++++++++++++++++++++



           


         

    •  

     

    對于jk2模塊的負(fù)載均衡配置可參見相關(guān)站點,值得提及的是jk2的負(fù)載均衡還支持權(quán)重分配等優(yōu)秀功能。


     

    2.3 配置tomcat

        同屬于一個集群下的兩個服務(wù)實體,要求功能的同一性,所以我們可先安裝和配置第一個tomcat,接著拷貝形成第二個tomcat,最后配置第二個tomcat。


     

    2.3.1 安裝第一個tomcat

        安裝tomcat 非常簡單,本文就不再描述。我們假設(shè)第一個tomcat的安裝路徑為d:\tomcat1。


     

    拷貝tomcat-javagroups.jar和javagroups.jar到d:\tomcat1\ server\lib 路徑下。


     

    2.3.2 配置第一個tomcat


     

    2.3.2.1 配置jk2


     

    tomcat 中的jk2 connector缺省端口為8009,為了在一臺機(jī)器上運行兩個tomcat,修改D:\Tomcat1\conf\jk2.properties,設(shè)置jk2 connector的端口為11009,整個文件內(nèi)容如下:


     
       
       
         
       
       
     


    #++++++++++++++

    channelSocket.port=11009

    #++++++++++++++


     

    2.3.2.2 修改server.conf


     

    首先為了讓一臺機(jī)器上運行兩個tomcat,修改server.conf的tomcat 停止指令監(jiān)聽端口:


     
       
       
         
       
       
     


    改為




     

    然后打開JK2 AJP connector ,關(guān)閉其它connector,下面是JK2 AJP 1.3的樣子,這里已把它的端口改為11009:


     
       
       
         
       
       
     




     
              port="11009" minProcessors="5" maxProcessors="75"

              enableLookups="true" redirectPort="8443"

              acceptCount="10" debug="0" connectionTimeout="20000"

              useURIValidationHack="false"

              protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"/>


     

    接著配置需要集群支持的webapp(比如examples) 的context,添加如下manager:


     
       
       
         
       
       
     



    className="org.apache.catalina.session.InMemoryReplicationManager"

    protocolStack="UDP(mcast_addr=228.1.2.3;mcast_port=45566;ip_ttl=32):PING(timeout=3000;

    num_initial_members=6):FD(timeout=5000):VERIFY_SUSPECT(timeout=1500):

    pbcast.STABLE(desired_avg_gossip=10000):pbcast.NAKACK(gc_lag=10;

    retransmit_timeout=3000):UNICAST(timeout=5000;min_wait_time=2000):

    MERGE2:FRAG:pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;

    shun=false;print_local_addr=false)">




     

    注意protocolStack的值必須在一行內(nèi)寫完。


     

    2.3.3 配置第二個tomcat


     

    我們先把已經(jīng)配好的第一個tomcat復(fù)制一份,形成第二個tomcat,假設(shè)路徑為d:\tomcat2。


     

    2.3.3.1 配置jk2


     

    修改D:\Tomcat2\conf\jk2.properties,設(shè)置jk2 connector的端口12009,整個文件內(nèi)容如下:


     
       
       
         
       
       
     


    #++++++++++++++

    channelSocket.port=12009

    #++++++++++++++


     

    2.3.3.2 修改server.conf


     

    有了第一個tomcat的配置我們只需修改server.conf的tomcat 停止指令監(jiān)聽端口:


     
       
       
         
       
       
     


    改為




     

    然后設(shè)置JK2 AJP connector 端口為12009。


     

    2.4 運行測試

        啟動apache,tomcat1和tomcat2。


     

    2.4.1 測試負(fù)載均衡


     

    我們先準(zhǔn)備兩個文件,第一個文件為test.jsp,拷貝到第一個tomcat 的根web應(yīng)用的目錄即d:\tomcat1\webapps\ROOT 下:


     
       
       
         
       
       
     








    <%= request.getSession().getId() %>

    Tomcat 1








     

    第二個文件也為test.jsp,拷貝到第二個tomcat 的根web應(yīng)用的目錄即d:\tomcat2\webapps\ROOT 下:


     
       
       
         
       
       
     








    <%= request.getSession().getId() %>

    Tomcat 2








     

    從不同的瀏覽器中多次輸入地址http://localhost/test.jsp 會看到不同的顏色,這表明apache中的jk2模塊起到了負(fù)載均衡的作用。


     

    2.4.2 測試錯誤恢復(fù)


     

    訪問url: http://localhost/examples/servlet/SessionExample 可以得到一個關(guān)于session的例子,我們用它來測試集群的錯誤恢復(fù)能力。


     

    測試步驟如下:


     

         
    1. 關(guān)閉tomcat1和tomcat2;
         
    2. 啟動tomcat1
         

    3.    
    4. 在瀏覽器中輸入屬性名tomcat1和屬性值tomcat1再提交,返回的頁面顯示session中有剛剛輸入的tomcat1屬性;
         
    5. 啟動tomcat2;
         
    6. 過一會后(等待tomcat2和tomcat1通信并復(fù)制信息)關(guān)閉tomcat1;
         
    7. 在瀏覽器中輸入屬性名tomcat2和屬性值tomcat2再提交,返回的頁面顯示session中有剛剛輸入的tomcat2屬性,還有先前輸入的tomcat1屬性;
         
    8. 啟動tomcat1;
         
    9. 過一會后(等待tomcat2和tomcat1通信并復(fù)制信息)關(guān)閉tomcat2;
         
    10. 在瀏覽器中輸入屬性名tomcat11和屬性值tomcat11再提交,返回的頁面顯示session中有剛剛輸入的tomcat11屬性,還有先前輸入的tomcat1和tomcat2屬性;

    11.  

     

    ……


     

    2.4.3 測試多目傳輸?shù)姆椒?/B>


     

    如果運行測試失敗,可以使用下面的JAVAGROUP方法測試機(jī)器的多目傳輸性:


     

    啟動多目接收器:


     
       
       
         
       
       
     


    java org.javagroups.tests.McastReceiverTest -mcast_addr 224.10.10.10 -port 5555


     

    啟動多目傳輸器:


     
       
       
         
       
       
     


    java org.javagroups.tests.McastSenderTest -mcast_addr 224.10.10.10 -port 5555


     

    這樣你在McastSenderTest窗口中輸入內(nèi)容,應(yīng)該在McastReceiverWindow中可以看到結(jié)果。如果看不到結(jié)果,在McastSenderTest運行參數(shù)中加入-ttl 32,如果還不行,可以修改多目地址再試試(注意避開系統(tǒng)保留用的多目地址);如果還不行,就去問問網(wǎng)管吧!


     

    2.4.4 對tomcat-javagroups的修改


     

    tomcat-javagroups.jar中的org.apache.catalina.session.ReplicatedSession類的removeAttribute方法會導(dǎo)致stackoverflow錯誤,請按下面的代碼對其進(jìn)行修改:


     
       
       
         
       
       
     


    public void removeAttribute(String name, boolean notify, boolean jgnotify) {

        super.removeAttribute(name);

        if ( jgnotify )

        {

            SessionMessage msg =

    new SessionMessage(notify?SessionMessage.EVT_ATTRIBUTE_REMOVED_WNOTIFY:SessionMessage.
                         
    EVT_ATTRIBUTE_REMOVED_WONOTIFY,

                                            null,

                                            getId(),

                                            name,

                                            null,

                                            null);

            sendMessage(msg);

        }


      }

      public void removeAttribute(String name, boolean notify) {

        removeAttribute(name,notify,true);

    }


     

    3 jetspeed集群

        我們現(xiàn)在知道了如何配置、甚至擁有一個集群環(huán)境,接下來本文分析Jetspeed的集群現(xiàn)狀,主要包括repository和Session數(shù)據(jù);為了使分析具有目的,在分析Jetspeed的集群現(xiàn)狀之前,先講述了集群需求和RunData對象。讀者可以用集群環(huán)境來驗證和調(diào)試Jetspeed的集群功能。


     

    3.1 集群要求


     

    《Memory Session Replication》一文中講述了支持集群的應(yīng)用程序需注意的要點,現(xiàn)在對關(guān)于應(yīng)用系統(tǒng)開發(fā)時應(yīng)注意的事項總結(jié)如下:


     

         
    1. 保存在Session中的對象必須實現(xiàn)java.io.Serializable接口;
         
    2. 從session中獲取對象修改后必須用session.setAttribute方法重置session中的屬性,因為只有setAttribute能導(dǎo)致session復(fù)制。
         
    3. Java VM不支持類變量的序列化,所以要注意failover不能依賴類變量;
         
    4. 保證各個服務(wù)實體的配置完全一樣;
         
    5. 保證session狀態(tài)是唯一決定當(dāng)前任務(wù)狀態(tài)的東西,臨時文件、類變量等會使得錯誤恢復(fù)難以實現(xiàn)、行為可能琢磨不定;
         
    6. 利用request.setAttribute()保存當(dāng)前請求級的狀態(tài),減少服務(wù)實體間通信次數(shù)。
         
    7. 盡量不要在session中保存大對象,提高服務(wù)實體間通信性能。

    8.  

     

    3.2 RunData對象


     

    RunData對象概念來自于Turbine,在Jetspeed中RunData對象的類型是DefaultJetspeedRunData,這個類擴(kuò)展了Turbine中的DefaultTurbineRunData類。Jetspeed系統(tǒng)接到用戶瀏覽器的URL請求,進(jìn)行計算和信息處理,最后返回給瀏覽器HTTP代碼流的整個過程中的代碼都可以訪問同一個RunData對象。所以RunData對象是Jetspeed系統(tǒng)中各個代碼模塊共享信息的機(jī)制。


     

    3.3 Jetspeed的Repository

        Repository 一般指一個軟件系統(tǒng)賴以啟動、運行的持久性環(huán)境,包括啟動Repository和運行Repository兩部分。啟動Repository用于決定系統(tǒng)啟動時的參數(shù),系統(tǒng)運行時不會改變它,如果改變了這些參數(shù),軟件系統(tǒng)必須重新啟動;運行Repository指實時影響軟件系統(tǒng)業(yè)務(wù)操作的參數(shù),這些參數(shù)可以被用戶或管理員當(dāng)系統(tǒng)在線時改變?,F(xiàn)在的趨勢是:盡量減少啟動Repository,而擴(kuò)大運行Repository;針對Repository的修改最好能使用管理性框架,比如SNMP和JMX。Jetspeed的repository主要在Xreg、psml和Properties文件中實現(xiàn)。


     

         
    1. Xreg是jetspeed的注冊表,用于登記portlet、control、controller、skin、mediatype等原始資源的定義,jetspeed中缺省地把它實現(xiàn)為文件形式,各種類型有自己的注冊表文件;
         
    2. Psml 是門戶結(jié)構(gòu)標(biāo)記語言的簡稱,用于組織xreg中的原始資源形成一個對門戶視圖的定義,當(dāng)用戶使用桌面瀏覽器訪問jetspeed系統(tǒng)時,這個系統(tǒng)根據(jù)用戶的URL定位一個Psml文檔,接著解釋這個文檔形成HTML代碼流返回給瀏覽器,瀏覽器展現(xiàn)這個代碼流從而形成視窗化的門戶視圖。Jetspeed中包括了對psml的數(shù)據(jù)庫和文件兩種實現(xiàn)方式;
         
    3. Properties定義了Jetspeed的重要服務(wù)及其參數(shù),目前只有文件實現(xiàn)方式。

    4.  

     

    Jetspeed的啟動Repository主要在Properties文件中,運行Repository在xreg和psml中。文件形式的實現(xiàn)大大阻礙了jetspeed支持集群的能力和表現(xiàn),因為現(xiàn)在很少的應(yīng)用服務(wù)器集群能在一個文件系統(tǒng)上運行,如果Repository需要在運行時改變,就必須同步多個服務(wù)實體上的文件,這是一個相當(dāng)麻煩的問題。如果Repository支持?jǐn)?shù)據(jù)庫實現(xiàn)形式,Jetspeed可以充分利用數(shù)據(jù)庫的存儲和同步機(jī)制實現(xiàn)同一個Repository服務(wù)于多個Jetspeed。所以要想 jetspeed支持集群、擁有更佳表現(xiàn),對Repository的數(shù)據(jù)庫化是一個不可忽視的任務(wù)。


     

    支持?jǐn)?shù)據(jù)庫的集群配置如下圖:


     

    image009.gifsrc="/tea/image/section/13739.gif" width=500 align=baseline vspace=5
    border=0>


     

    這個圖顯示了在數(shù)據(jù)庫集群環(huán)境下的jetspeed集群配置,數(shù)據(jù)庫負(fù)載均衡器實現(xiàn)數(shù)據(jù)庫集群的單一影像,例子有weblogic server中的multipool datasource,sql server 基于的windows 2000集群的單一集群IP,ORACLE RAC 的支持多連接地址的thin jdbc driver。


     

    3.4 Jetspeed的Session數(shù)據(jù)

        支持集群必須使得各個服務(wù)實體針對某個任務(wù)的執(zhí)行環(huán)境是相同的,對于jetspeed來說就是針對各個URL請求,session的數(shù)據(jù)能在各個jetspeed上復(fù)制。這些session被同一個sessionid所標(biāo)識,這些標(biāo)識可能來自瀏覽器的cookies或URL中。我們首先用一個velocityportlet來顯示Jetspeed的session中到底保存了什么數(shù)據(jù),這個portlet的注冊名字為SessionPortlet。


     

    3.4.1 SessionPortlet


     

    SessionPortlet是一個velocityPortlet,其類名可以是CustomizerVelocityPortlet或VelocityPortlet,一般情況下沒有必要開發(fā)一個新的portlet class。關(guān)于如何開發(fā)部署portlet的教程可見參考部分,現(xiàn)在我們分注冊、控制助手、portlet模版和運行來講述這個portlet。


     

    3.4.1.1 注冊


     

    SessionPortlet用于顯示目前的session數(shù)據(jù)。它在xreg中的注冊代碼為:


     
       
       
         
       
       
     





     

    3.4.1.2 控制助手Action


     

    portlets.SessionAction是Velocityportlet模版portlet的控制助手,在velocity解釋模版前執(zhí)行:


     
       
       
         
       
       
     


    public class SessionAction extends VelocityPortletAction {

    protected void buildNormalContext( VelocityPortlet portlet,

    Context context,

    RunData rundata )

    {

    Map map = new HashMap();

    Enumeration enumeration = rundata.getSession().getAttributeNames();

    while (enumeration.hasMoreElements()) {

    Object key = (Object) enumeration.nextElement();

    Object value = (Object)rundata.getSession().getAttribute(key.toString());

    map.put(key, value);

    }

    context.put("sessions",map);

    }

    }


     

    從上面的代碼可以看出,這個控制助手在模版的模型(MVC中的M)環(huán)境中設(shè)置了一個保存了session數(shù)據(jù)的map數(shù)據(jù)結(jié)構(gòu)。


     

    3.4.1.3 portlet模版


     

    SessionPortlet的模版文件是session.vm(MVC中的V),這個文件的內(nèi)容如下:


     
       
       
         
       
       
     




      #foreach( $key in $sessions.keySet() )

    • Key: $key -> Value: $sessions.get($key)


    • #end




     

    3.4.1.4 定制psml和運行SessionPortlet


     

    用admin/jetspeed或turbine/turbine帳號/口令登錄到j(luò)etspeed系統(tǒng)后,可以在velocity.legend portlet分類中找到SessionPortlet,把它加入到你的psml中后可以看到SessionPortlet顯示的session數(shù)據(jù)(你可以多多點擊其它的URL,盡量地使jetspeed在session中多放一些數(shù)據(jù)):


     

    image012.gifsrc="/tea/image/section/13740.gif" width=500 align=baseline vspace=5
    border=0>


     

    從上面的session快照可以看出,Jetspeed的session數(shù)據(jù)主要分為兩類:BaseJetspeedUser和JetspeedHttpStateManagerService$StateEntry,下面我們就分別來看看這兩個類的情況。


     

    image015.gifsrc="/tea/image/section/13741.gif" width=500 align=baseline vspace=5
    border=0>


     

    3.4.2 BaseJetspeedUser


     

    我們從《Session數(shù)據(jù)類圖(部分)》可以看出BaseJetspeedUser實現(xiàn)了serializable接口。另外分析這個類及其父類的代碼可了解到這個類的成員也實現(xiàn)了serializable接口。所以可以初步得出這個類是集群安全的。


     

    DefaultTurbineRundata實現(xiàn)了這個類型的session數(shù)據(jù)的操作接口:


     

         
    • 保存user對象到session中,這個方法登錄后由TurbineAuthentication的login調(diào)用,登錄前由JetspeedSessionValidator的doPerform調(diào)用,它們同時會調(diào)用DefaultTurbineRundata的setUser方法:
           
             
             
               
             
             
           


      public void save()

        {

          session.putValue(User.SESSION_KEY, (Object) user );

      }

      public void setUser(User user)

      {

        this.user = user;

      }


         
    • 從session中獲取user對象數(shù)據(jù),這個方法由JetspeedSessionValidator的doPerform調(diào)用:
           
             
             
               
             
             
           


      public void populate()

        {

          user = getUserFromSession();


          if ( user != null )

          {

              user.setLastAccessDate();

              user.incrementAccessCounter();

              user.incrementAccessCounterForSession();

          }

        }

        public User getUserFromSession()

        {

          return getUserFromSession(session);

        }


        public static User getUserFromSession(HttpSession session)

        {

          try

          {

              return (User) session.getValue(User.SESSION_KEY);

          }

          catch ( ClassCastException e )

          {

              return null;

          }

        }


         
    • 刪除session中的用戶數(shù)據(jù),目前沒地方調(diào)用:
           
             
             
               
             
             
           


        public boolean removeUserFromSession()

        {

          return removeUserFromSession(session);

        }

          public static boolean removeUserFromSession(HttpSession session)

        {

          try

          {

              session.removeValue(User.SESSION_KEY);

          }

          catch ( Exception e )

          {

              return false;

          }

          return true;

      }


         

    •  

     

    3.4.2.1 用戶登錄


     

    用戶在jetspeed的首頁中輸入用戶名和口令,接著點擊登錄(login)按鈕,可以激活JLoginUser.doPerfom->TurbineAuthentication.login->DefaultTurbineRundata.save->JetspeedSessionValidator.doPerform-> DefaultTurbineRundata.populate系列步驟。


     

    如果properties配置中的配置項automatic.logon.enable 的值為true,JLoginUser.doPerfom還會設(shè)置瀏覽器cookies:username 和logincookie。username是成功登錄的用戶名, logincookie是一個隨機(jī)值,會保存到用戶數(shù)據(jù)庫中。


     

    當(dāng)用戶訪問jetspeed的首頁時,JetspeedSessionValidator.doPerform檢查RunData對象中的當(dāng)前用戶,如果沒有登錄而且automatic.logon.enable 的值為true,它會從cookies中獲取username 和logincookie,再從用戶數(shù)據(jù)庫中查尋用戶的logincookie,如果它們相等則調(diào)用下面的代碼設(shè)置RunData的用戶數(shù)據(jù):


     
       
       
         
       
       
     


    data.setUser(user);

    user.setHasLoggedIn(new Boolean(true));

    user.updateLastLogin();

    data.save();


     

    至于針對不同的用戶,首頁中顯示的portlet由缺省screen模版中調(diào)用JetspeedTool的方法(有一套PSML定位算法)來決定。


     

    3.4.2.2 當(dāng)session過期之后顯示匿名用戶的主頁


     

    當(dāng)session過期,Turbine.doget首先會創(chuàng)建新的session,接著激活JetspeedSessionValidator.doPerform-> JetspeedSecurity.getAnonymousUser->DefaultTurbineRundata.save系列步驟。


     

    JetspeedSessionValidator.doPerform會設(shè)置缺省screen模版。


     

    3.4.2.3 用戶登出


     

    當(dāng)用戶登錄之后,點擊Jetspeed系統(tǒng)右上角的登出(logout)按鈕,可以激活JLogOut.doPerform-> TurbineAuthentication.logout-> TurbineAuthentication.getAnonymousUser-> DefaultTurbineRundata.save系列步驟。


     

    TurbineAuthentication.getAnonymousUser從數(shù)據(jù)庫中得到匿名用戶的用戶數(shù)據(jù)(根據(jù)properties配置中user.anonymous項)。


     

    如果properties配置中配置項automatic.logon.enable 的值為true,JLogOut.doPerform還會刪除瀏覽器和當(dāng)前request的cookies:username 和logincookie,防止后面的JetspeedSessionValidator拿著先前的用戶數(shù)據(jù)自動登錄。JLogOut.doPerform最后設(shè)置data的缺省screen模版。


     

    3.4.3 JetspeedHttpStateManagerService$StateEntry


     

    我們從《Session數(shù)據(jù)的類圖(部分)》可以看出StateEntry沒有實現(xiàn)了Serializable接口。把它放到session的屬性中不是集群安全的。Serializable接口只是個標(biāo)志接口,它不擁有任何函數(shù)和數(shù)據(jù)成員,


     

    為了使其集群安全化,首先必須讓StateEntry實現(xiàn)Serializable接口。


     

    DefaultJetspeedRunData擁有下列對StateEntry類型的session數(shù)據(jù)操作接口:


     

         
    • 用戶session接口,獲取保存用戶session數(shù)據(jù)的SessionState。這個SessionState保存的session數(shù)據(jù)以"org.apache.jetspeed.services.statemanager.JetspeedHttpStateManagerService"+ sessionID為key。
           
             
             
               
             
             
           


      public SessionState getUserSessionState()

        {

          StateManagerService service = (StateManagerService)TurbineServices

                .getInstance().getService(StateManagerService.SERVICE_NAME);

          if (service == null) return null;

          return service.getSessionState(getSession().getId());

        }


         
    • request的Session接口,獲取保存當(dāng)前request session數(shù)據(jù)的SessionState。這個SessionState保存的session數(shù)據(jù)以"org.apache.jetspeed.services.statemanager.JetspeedHttpStateManagerService"+ (sessionID和profileID組成的pageSessionID)為key。
         
           
             
             
             
           
         


        public SessionState getPageSessionState()

        {

          StateManagerService service = (StateManagerService)TurbineServices

                .getInstance().getService(StateManagerService.SERVICE_NAME);

          if (service == null) return null;

          return service.getSessionState(getPageSessionId());

        }


         
    • Portlet的Session接口,獲取保存Portlet session數(shù)據(jù)的SessionState。這個SessionState保存的session數(shù)據(jù)以"org.apache.jetspeed.services.statemanager.JetspeedHttpStateManagerService"+ pageSessionID+portletID為key。
         
           
             
             
             
           
         


        public SessionState getPortletSessionState(String id)

        {

          // get the StateManagerService

          StateManagerService service = (StateManagerService)TurbineServices

                .getInstance().getService(StateManagerService.SERVICE_NAME);

          if (service == null) return null;

          String pageInstanceId = getPageSessionId();

          return service.getSessionState(pageInstanceId + id);

        }


         

    •  

     

    3.4.3.1 類圖


     

    image018.gifsrc="/tea/image/section/13742.gif" width=500 align=baseline vspace=5
    border=0>


     

    BaseStateManagerService有一個類型為Map的成員變量m_httpSessions,以Thread對象為key,HttpSession對象為值。HttpSession對象中屬性的key 是前面DefaultJetspeedRunData的StateEntry類型的session數(shù)據(jù)操作接口的key,屬性的值為StateEntry對象。StateEntry對象的成員變量m_key保存操作接口的key,成員變量m_map是一個Map對象,以后面我們要講的setAttribute方法的name參數(shù)為 key,value參數(shù)為值。


     

    3.4.3.2 初始化


     

    下面的順序圖是一個簡圖,主要用于解釋BaseStateManagerService的成員變量m_httpSessions的映射如何被填充和清除。


     

    image021.gifsrc="/tea/image/section/13743.gif" width=500 align=baseline vspace=5
    border=0>


     

    Turbine是一個servlet,其doGet方法是jetspeed系統(tǒng)的入口。


     

         
    • 填充
           


           

      Turbine請求JetspeedRunDataService生成RunData對象,JetspeedRunDataService調(diào)用HttpServiceRequest的getSession(true)方法獲取與當(dāng)前請求對應(yīng)的httpSession對象(以true為參數(shù),getSession在當(dāng)前session無效時會返回一個新的httpSession對象,否則返回先前請求的httpSession對象),JetspeedRunDataService接著調(diào)用JetspeedHttpStateManagerService的setCurrentContext(httpSession對象)方法,這個方法會以當(dāng)前的Thread為key,參數(shù)httpSession對象為值填充BaseStateManagerService的成員變量m_httpSessions。


         
    • 清除
           

      doGet方法填充了m_httpSessions,并作了好多事情之后,在即將退出之前調(diào)用了JetspeedRunDataService的putRunData(data)方法,這個方法再調(diào)用JetspeedHttpStateManagerService的clearCurrentContext()方法刪除BaseStateManagerService的成員變量m_httpSessions中以當(dāng)前Thread為key的Map項。

              下圖顯示了m_httpSessions對象經(jīng)過初始化后的內(nèi)存狀態(tài)快照,體現(xiàn)了m_httpSessions對象保留的Thread-〉HttpSession的映射關(guān)系。


         

    •  

     

    image023.gifsrc="/tea/image/section/13744.gif" width=500 align=baseline vspace=5
    border=0>


     

    3.4.3.3 屬性操作


     

    當(dāng)DefaultJetspeedRunData通過session操作接口獲取SessionState之后,其它就可以使用SessionState對象的成員方法操作狀態(tài)屬性了。這兩個方法是:


     
       
       
         
       
       
     


    public void setAttribute( String name, Object value );

    public void removeAttribute( String name );


     

    image026.gifsrc="/tea/image/section/13745.gif" width=500 align=baseline vspace=5
    border=0>


     

    在從RunData對象處獲取sessionState對象后,jetspeed代碼可以調(diào)用這個對象的屬性操作方法。


     

         
    • setAttribute(name,value)操作的大概步驟是:
           

      (1) 主要步驟:


           

      1.1sessionState對象利用自己的key,結(jié)合參數(shù)name,value調(diào)用JetspeedHttpStateManagerService的setAttribute(key,name,value)方法;


           

      1.1.1JetspeedHttpStateManagerService調(diào)用自己的getState(key)方法在參數(shù)key的幫助下獲取保存在當(dāng)前線程session中的StateEntry對象的m_map變量,這個過程由1.1.1.1-1.1.1.4組成;


           

      1.1.2得到StateEntry對象的m_map變量后,JetspeedHttpStateManagerService接著先處理m_map中的先前的參數(shù)name對應(yīng)的屬性值,再設(shè)置參數(shù)name對應(yīng)的屬性值新值為參數(shù)value。


           

      (2) 候選步驟:


           

      1.1.1a 如果session中沒有相應(yīng)的StateEntry對象,則先生成并往一個session中加入一個。


         
    • getAttribute(name)操作的大概步驟是:
           

      (1) 主要步驟:
           

      2.1sessionState對象利用自己的key,結(jié)合參數(shù)name調(diào)用JetspeedHttpStateManagerService的getAttribute(key,name)方法;


           

      2.1.1JetspeedHttpStateManagerService調(diào)用自己的getState(key)方法在參數(shù)key的幫助下獲取保存在當(dāng)前線程session中的StateEntry對象的m_map變量;


           

      2.1.2得到StateEntry對象的m_map變量后,JetspeedHttpStateManagerService接著調(diào)用m_map對象的get(name)方法獲取屬性值。


         

    •  

     

    下圖體現(xiàn)了這些方法執(zhí)行后HttpSession對象保留的key-> StateEntry對象以及StateEntry對象的Name->Value的映射關(guān)系。


     

    image028.gifsrc="/tea/image/section/13746.gif" width=500 align=baseline vspace=5
    border=0>


     

    3.5 修改建議


     

    (1) 實現(xiàn)數(shù)據(jù)庫形式的repository。根據(jù)前面的集群需求第五條,必須把repository數(shù)據(jù)庫化才能使得集群下的各個jetspeed的資源視圖相同。


     

    (2) StateEntry。根據(jù)前面的集群需求第一條,必須讓StateEntry實現(xiàn)Serializable接口。目前StateEntry是一個內(nèi)部類,為了讓JVM的Serializer設(shè)施能順利創(chuàng)建StateEntry對象,最好把其public化。


     

    (3) setAttribute要重設(shè)session屬性。根據(jù)前面的集群需求第二條,session對象的setAttribute是導(dǎo)致復(fù)制的引子,我們必須在改變session屬性后調(diào)用session對象的setAttribute方法重置session屬性,如下圖所示。


     

    image030.gifsrc="/tea/image/section/13747.gif" width=500 align=baseline vspace=5
    border=0>


     

    雖然Jetspeed中這樣模式的代碼如下:


     

         
    • 更改JetspeedHttpStateManagerService的setAttribute方法。

    •  

     

    對下面類中的doXXX方法按照這個模式進(jìn)行修改。


     

         
    • controllers.MultiColumnControllerAction;
         
    • portlets.CustomizeSetAction;
         
    • controllers.RowColumnControllerAction;
         
    • 注意StateEntry中的值的序列性。

    •  

     

    4 總結(jié)

        可以這樣說,目前的jetspeed在設(shè)計和實現(xiàn)時沒有考慮集群環(huán)境下的運行情況,本文的分析突出了jetspeed支持集群的主要癥結(jié)、但不一定完善,甚至有不正確的地方,另外一個主要內(nèi)容是分析jetspeed保存在session對象中的數(shù)據(jù)。希望本文有助于大家加深對集群的理解,有助于提醒大家在設(shè)計和開發(fā)軟件系統(tǒng)時"keep clustering in mind"。


     
     

    參考資料


     
     

    本文轉(zhuǎn)載自target=_blank>IBM developerWorks,版權(quán)歸IBM developerWorks和原作者所有。


     
     

    相關(guān)文章
    本頁查看次數(shù):