讀萬卷書不如行千里路,經驗的積累又不是一蹴而就的,不但需要知識的沉積,還需要長久經驗的總結升華

          常用鏈接

          統計

          積分與排名

          AJAX相關站點

          java壓力、性能等測試工具

          開源軟件基地

          最新評論

          2006年4月4日 #

          IBM WebSphere 自定義門戶代碼的性能注意事項

          性能通常是功能、可維護性、程序執行時間和內存使用間的折衷。本文檔提供了一些編程建議,以使用 IBM? WebSphere? Portal 最大限度地提高門戶應用程序組件(如 Portlet、主題和外觀)的性能。

          摘自 IBM WebSphere 開發者技術期刊

          引言

          本文提供了一些常規指導,以創建能夠正常執行的 IBM WebSphere Portal 自定義代碼。自定義代碼不僅指 Portlet(雖然它們是最常見的門戶編程模型),也包括 WebSphere Portal 的主題和外觀代碼。由于這些組件均采用與 Portlet 相同的技術實現,所以很多相同的性能注意事項對它們也同樣適用。

          對于 Portlet,本文主要集中討論遵循 Java? Portlet 規范 JSR 168 及 WebSphere Portal 中的對應實現的標準化 Portlet。本文基于 WebSphere Portal V5.1 或更高版本,不過本文中所給出的準則和大部分建議永遠適用,而不受所運行的 WebSphere Portal 版本的影響。

          本文將說明如何設置和使用 Porlet 應用程序的部署參數以優化門戶和 Portlet 的性能,因為這是創建自定義門戶代碼的最后一步。不過 WebSphere Portal 的總體優化(即創建并部署自定義代碼后執行的管理操作)將不在本文中討論。另一個文檔對 WebSphere Portal 性能優化進行了說明。該文檔與本文檔共同提供了門戶與性能方面不錯的參考資料。

          本文旨在供參與構建門戶應用程序并希望提高對與自定義代碼相關的潛在性能問題的理解的程序員、設計人員和架構師使用。

          WebSphere Portal 環境概述

          IBM WebSphere Portal 構建于 the IBM WebSphere Application Server 產品之上。因此,自定義門戶代碼的編程環境具有三重特征,其對應的重要含義如下:

          • WebSphere Portal 及其所有組件均為基于 Java 的程序。

            因此,總的來說,應該遵循編寫高性能 Java 代碼的最佳實踐。

          • WebSphere Portal 是運行于應用程序服務器平臺上的 J2EE 應用程序。

            J2EE 包含多線程技術;J2EE 容器通常采用每個請求一個線程的方法處理請求負荷。對于使用此機制必然涉及到的任何實現或性能注意事項都應該加以注意。

          • WebSphere Portal 提供了 API 以擴展門戶功能。

            可以采用很多方法對任務進行編程。應該首先考慮影響性能的差異。

          下一部分中將介紹一些關于門戶編程環境不同部分的一般性能注意事項。

          Java

          顯然,本部分并不會提供處理 Java 性能的全部技術。我們僅在此處給出在我們認為進行 WebSphere Portal 開發時最有用的相關事項,并提供一些可幫助深入了解 Java 性能的參考資料(請參閱參考資料)。

          基本 Java 性能

          在這一部分中,我們將討論一些應用到大部分 Java 的一般性能項。盡管這些建議可能并不會帶來大幅度的性能提高,但可以使您對在開發階段底層程序執行性能的重要性有所認識。

          • 當需要修改字符串時,使用 java.lang.StringBuffers 實例,而不要使用 java.lang.String 實例。

            在 Java 中,String 對象是不可變的,而 StringBuffer 對象是可變的。無論何時將文本附加到 String 或從中刪除,實際上都將創建一個新對象,并將舊對象丟棄。因此我們首選以下的方式:

            																
            																		StringBuffer sb = new StringBuffer("Hello ");
            sb.append(var).append(" World");
            																
            														

            以此為基礎的字符串聯結操作:

            																
            																		String s = "Hello" + var + " World";
            																
            														

            有時可以通過設置 StringBuffer 的初始容量進一步提高性能;該類的設計使其可以在不能保存全部數據時自動擴大容量。不過此處有性能損失,因為 StringBuffer 必須透明地增加其大小和對數據移位。例如,如果將 StringBuffer 作為收集參數(即將向其添加越來越多的數據)使用,應該在對其進行初始化之前計算恰當的緩沖區大小,以使其永遠都不需要增加大小。

          • 避免在服務器端程序中進行開銷很大的 I/O 操作。

            在 I/O 操作期間至少會阻塞當前線程;如果其他線程也必須等待磁盤,則系統響應時間將會迅速增大。除非在執行日志記錄功能(例如,在記錄異常或站點訪問信息),否則 WebSphere Portal 自己不會引起任何磁盤訪問。我們將在后面對 I/O 進行進一步討論。

          • 盡可能減少同步代碼塊的數量和長度。

            synchronized 關鍵字每次僅允許一個線程進入代碼塊。同步代碼塊所需的執行時間越長,其他線程等待進入該代碼塊的時間就越長。我們將在后面對同步進行進一步討論。

          • 避免開銷巨大的計算和方法調用。

            例如,使用 System.currentTimeMillis() 檢索當前時間信息開銷就相當大。如果確實需要時間信息,請確定是需要當前準確的時間,還是(例如)準確到最近的秒數就足夠了。如果在代碼路徑中有很多獲取時間的調用,但并非一定要毫秒級的準確度,可以采用替換方法,即確定請求開始的時間,然后直接在請求期間使用該信息。

          • 限制異常的使用。

            通常,應將 Java 中的異常用于指示錯誤情況。不要使用異常指示操作成功,這主要是因為 JVM 創建異常堆棧跟蹤非常費時,而且在 WebSphere Portal 系統中的跟蹤深度會很深。

          • 使用 Java Reflection API 時需要謹慎。

            此 API 為動態代碼執行增加了功能強大的選項,但就方法執行時間而言,獲得這種靈活性會導致嚴重的性能損失。通常,應盡力避免在門戶代碼中使用 Java Reflection API。不過,如果有必要進行反射調用,則應盡量將其放置在初始方法中,以使其在每個請求期間都不會執行。

          內存使用和垃圾生成

          雖然內存對于 Java 客戶機軟件通常不是一個突出的性能問題,但對于 J2EE 應用程序卻是一個主要問題,這主要是因為企業應用程序通常由很多用戶同時訪問。為了使應用程序服務器高效運行,可用資源(包括內存、CPU 和帶寬)均由客戶機的請求共享。我們要提到三個主要內存問題:

          • 盡可能減少臨時對象的數量。

            這意味著要盡可能地重用對象,而不要太頻繁地創建新對象實例。創建的對象越多,JVM 垃圾回收器就必須更頻繁地回收內存并會(至少部分)中斷此時的請求處理。創建許多對象還容易增加堆碎片,而這會導致出現更多的垃圾回收周期。例如,不要過早創建對象:

            																
            																		String logData = "Parameter 1: " + param1;
            if (logger.isLogging(DEBUG)) {
            	logger.log(logData);
            }
            																
            														

            在本例中,僅在對條件進行求值之后才應創建 logData。緩存和對象池技術均可以減少臨時對象的創建。若要識別代碼中最常導致內存分配問題的部分,請參閱工具

          • 將內存永久占有的情況保持為最低。

            不要將太多信息讀入內存中;而要使用緩存保存重要的信息。有時可以針對一條信息更改數據類型。例如,數據信息可以保存在 java.util.Date 對象內或 long 變量中。與基元數據類型相比,對象通常更大,處理速度也會有些慢。它可能會依賴于鄰近的 API 和數據類型首選的數據結構。通常,內存占用率越高,就會導致垃圾回收率更高,請求處理期間暫停的次數也會增加。

          • 檢查應用程序以確定是否存在內存泄漏。

            內存泄漏通常出現在 Java 集合類中。例如,如果有一個 java.util.Map,在特定情況下,會將數據添加到映射中,但卻永遠不從其中刪除。內存泄漏會導致 Java 堆保留的內存使用越來越大,隨著時間的增加,垃圾回收器能釋放的內存會越來越少。這樣,會導致垃圾回收更頻繁,而最終將使門戶系統停止響應。而更糟糕的是,通常僅在長時間運行的測試中才能發現內存泄漏,不過可以使用各種工具幫助進行此類分析(請參閱工具)。

          性能和可擴展性代碼設計

          設計和開發可伸縮性代碼時,需要記住很多事項。其中最為重要的三方面是:緩存、對象池和信息預提取:

          • 緩存,存儲已經計算得到的結果。

            例如,可以從后端系統檢索信息,但不將每個可能的對象均從存儲區復制到內存中,而僅加載其中的小部分,將其放置在緩存中。這樣,該信息就對稍后的引用可用(可能在后續的另一請求中使用,甚至供另一個用戶使用)。

            緩存始終采用對象映射的形式,具有大小上限。緩存還必須知道不可能再次請求某個內容的情況,以便在合適時從緩存中將其刪除。這種排除操作通常由“生存時間”(TTL) 或“最近最少使用”算法確定。而且使用緩存的客戶機不能保證將成功從緩存檢索對象;必須首先檢查對象是否存在,如果沒有找到,則將創建該對象:

            																		
            																				Mail mail = myCache.get("myMail");
            if (mail == null) {
            	mail = readMailInformation();
            	myCache.put("myMail", mail) ;
            }
            ...
            																		
            																

            (在某些情況下,特定于應用程序的緩存可以設計為從對客戶機透明的某個數據源查找所需的數據。)

          • 使用對象池限制特定類的實例的數量。

            每個請求都需要特定類的實例,但此對象并不(也不應)需要在每個請求中重新創建。在對象創建和初始化開銷很大的情況下,尤其是這樣。客戶機可以不接受性能命中,而從池中請求對象,然后在用完之后將其返回池中。

            																		
            																				PooledObject po = myPool.get();
            ...
            // use the PooledObject 
            ...
            myPool.put(po);
            																		
            																

          • 對象池的一種簡單形式就是將對象規范化。

            這意味著對象的所有不同實例在程序初始化階段創建,將在隨后重用和引用。java.lang.Boolean 類就是已規范化對象的例子。只需要有兩種不同的 Boolean 對象即可(最好能作為常數訪問)。同樣,也可使其他對象使用一組固定的只讀內部狀態。

          • 只提取當前希望處理的數據,而不提取多余的數據。

            例如,在 Portlet 中,可以提供一個電子郵件列表;該 Porlet 將顯示主題、日期、發件人和其他重要信息。當用戶選擇了特定的電子郵件時,將顯示該郵件的正文。在從 Porlet 中選擇特定的項之前,不需要正文,因此提前檢索正文將浪費執行時間和內存資源。這種模式在很多情況下都適用。總的原則是,僅計算和檢索對于當前請求和響應有直接意義的信息。

          J2EE

          IBM WebSphere Application Server 是 J2EE 實現,WebSphere Portal 就構建于其上。由于本部分中很多性能注意事項適用于 J2EE 運行時上下文,所以其中的很多信息除了適用于 WebSphere Application Server 之外,也適用于其他應用程序服務器和 J2EE 應用程序。下面所列出的項目在此處只進行了簡單概述,將在后面進行更為詳細的說明。有關更多的一般性討論,請參閱參考資料

          J2EE 標準

          J2EE 標準規范包含了大量與性能相關的事項:

          • 應當使用初始方法計算所有后面將用到且不會發生更改的內容(很多 J2EE 資源都可以使用初始方法,Portlet 也可以使用此類方法)。例如,數據源等普通資源的 JNDI 程序應該僅在初始化時執行一次。此外,也應該僅在 Portlet 初始化期間讀取一次來自特定只讀文件的數據。可以對 Portlet 服務方法進行掃描,以發現所有對每個請求執行相同操作的代碼,將其移動到初始方法中,以降低該服務方法的運行時開銷。

          • EJB 和會話是 J2EE 中非常重要且功能強大的概念,但如果使用不當,二者均可能導致性能損失。例如,應用程序不應將過多的數據放置到會話中,從而減少服務器的內存占用并更快速方便地保持會話。關于 EJB 組件,應該熟悉與遠程調用和本地調用等相關的不同持久類型。EJB 可以使用的某些功能會帶來大的性能損失。

          WebSphere Application Server

          WebSphere Application Server 產品提供了各種功能,以幫助開發人員和架構師設計高性能系統。(請參閱參考資料中給出的 WebSphere Application Server 信息中心和 WebSphere Business Integration Server Foundation 信息中心)。

          • 正如前面所提到的,創建數據庫連接的開銷非常大。按照 J2EE 標準中的定義,應用程序服務器可以提供連接池機制,從而無需為每個傳入請求重新創建連接。WebSphere Application Server 使用一些額外的性能 Helper 提供這樣的連接池機制,相當于頻繁執行的 SQL 語句的語句緩存。不過,如果完成數據庫交互后沒有及時返回連接,將回導致相當長時間內連接對其他請求不可用。通過使用 WebSphere Application Server 管理控制臺,可以將連接池作為 JDBC 數據庫的數據源屬性進行控制,并能進行定義,例如可以定義連接池中的最少連接數和最大連接數。(請參閱 WebSphere Application Server 信息中心以了解詳細信息。)

            下面所給的示例演示了多個請求對連接進行重用。在本例中,可以使用 JDBC 連接池和利用 Application Server 提供的語句緩存:

            . . . 
            public class IDontCare extends GenericPortlet {
                  
               private javax.sql.DataSource ds;
               
               public void init() throws javax.portlet.PortletException {
                  
                  try {
                     Hashtable env = new Hashtable();
                     env.put( Context.INITIAL_CONTEXT_FACTORY,
                        "com.ibm.ejs.ns.jndi.CNInitialContextFactory" );
                     Context ctx = new InitialContext( env );
                     ds = (javax.sql.DataSource)ctx.lookup( "jdbc/MYSHOES" );
                     ctx.close();
                  } catch (Exception any) {
                     // handle exceptions here
                     . . . 
                  }
               }
            
               . . . 
               public void processAction ( 
                  ActionRequest request, 
                  ActionResponse response
               ) throws PortletException, IOException {
                  . . . 
                  try {
                     Connection con = null;
                     ResultSet rs = null;
                     PreparedStatement pStmt = null;
                     con = ds.getConnection ( dbuser, dbpasswd );
                     pStmt = con.prepareStatement(
                        "select * from myscheme.size_of_shoes");
                     rs = pStmt.executeQuery(); 
                     . . . 
            
                     // release the resource when it is no longer used
                     if (pStmt != null) pStmt.close();
                     if (con   != null) con.close();
                  } catch (Exception any) {
                     // handle exception here
                     . . . 
                  } 
               }
            }

          • WebSphere Application Server 還支持對象池的常規概念,每個對象池均具有池管理器,從而為不同類類型提供對象池訪問。可以查詢此類對象池以獲得類類型實例,如前面關于池技術的示例中所述。請參閱 WebSphere Business Integrator Server Foundation 信息中心以獲得詳細信息。

          • WebSphere Application Server 還提供了“一般用途”的緩存。在管理控制臺中,可以定義緩存實例,應用程序可以使用這些緩存實例存儲、檢索和共享數據。與缺省共享動態緩存(門戶使用其緩存對象)不同,緩存實例僅可由知道其 JNDI 名稱的應用程序訪問。DistributedMap 類是應用程序所使用的編程接口,該類允許應用程序從緩存實例獲得對象和將對象放置到其中,并可以使其失效。請參閱 WebSphere Business Integrator Server Foundation 信息中心以獲得詳細信息 [8] (LINK)。

            如果 Portlet 使用緩存實現,它們應該在其初始階段查找或實例化一個緩存實例,并保持該緩存的引用,以使緩存條目具有可能比單個請求長的生存期。在處理 Portlet 的操作和呈現階段時,可以將條目放置到緩存中,并從中進行檢索。Portlet 實現需要確保如果使用特定鍵查詢時,緩存沒有返回數據,應有適當的后端訪問和緩存進行更新處理。另外,還要注意,為了實現設計的特定功能,可能需要限定鍵在緩存中的范圍(如,基于用戶會話)。緩存通常為自我管理的單元,根據緩存實現,可以排除條目或使其失效。請注意,出于同樣的原因,緩存并不適合在多段代碼間進行信息通信。緩存還應該維護一個合理的大小上限,以避免自定義代碼中內存的過度使用。

          門戶 API

          WebSphere Portal 支持兩種不同的 Portlet API:

          • IBM Portlet API,該 API 對 Servlet 進行擴展。
          • JSR 168 Portlet API,該 API 由 Java Community Process (JCP) 定義。

          在本文中,我們將重點討論 JSR 168 Portlet API。

          WebSphere Portal 提供了各種接口,用于將 Portlet 集成到 WebSphere Portal 環境中。因此,應該謹慎設計 Portlet,以充分利用各種門戶功能。請確保采用最佳實踐(請參閱參考資料中列出的最佳實踐),以應用恰當的 WebSphere Portal API。

          常見的實現注意事項

          在本部分中,我們將討論與主題和外觀編程以及 Portlet 開發相關的性能主題。

          JSP

          JavaServer Page (JSP) 是 Portlet 編程的基礎之一。在大多數 Portlet 中,JSP 通過使用 Model View Controller (MVC) 作為視圖組件使用。JSP 由 HTML(或其他標記語言)組合和 Java 代碼組成;在大多數 HTML 中,它們的處理輸出也是標記語言。其最簡單的形式中,JSP 不包含任何 Java 代碼,但僅包含自定義標記,調用這些標記以執行非 HTML 操作。(相反地,JSP 文件中也可能不包含任何 HTML 內容)。

          • 在第一次訪問 JSP 文件時,將對文件進行分析,將其轉換為常規 Java Servlet 源文件,該源文件將隨后編譯為字節代碼。因此,由于后續的兩次轉換(從 JSP 到 Java 源再到字節代碼),第一次請求 時通常比較緩慢,但對于之后的所有請求,JSP 將同任何其他 Servlet 一樣工作。

            這與其他生成 HTML 內容的方法(XML 和 XSLT)不一樣。使用其他方法時,對于每次請求都必須解析 XML 和應用樣式表轉換。只有很好地緩存了結果而不需每次請求都重新運行轉換,才能保證性能。因此,從性能的角度出發,JSP 應該優于 XML/XSLT。此外,門戶基礎設施還針對 JSP 進行了優化,允許方便地擴展以支持其他標記、語言和瀏覽器。

          • 應用程序服務器執行 JSP 的方式與執行常規 Servlet 類似。不過,JSP 編譯產生的 Servlet 包含生成的代碼,這些代碼的性能優化程度稍遜于手動編寫的代碼。如果性能對于特定 JSP 非常重要,而使用生成的代碼又不能達到目的,請考慮手動將標記編寫到輸出流中。

          • JSP 中的 Java 代碼片斷稱為 Scriptlet。由于 JSP 將轉換為 Java 源代碼,因此使用 Scriptilet 并沒有真正的性能損失。WebSphere Application Server 的最新版本中的某些優化將在 JSP 文件不包含任何 Scriptlet 的情況下應用。通常,不應將 Scriptlet 代碼放置到 JSP 中,而應使用標記完成這些任務。

          • JSP 中可以包含其他 JSP。這意味著單個 JSP 不必對請求作出全部響應;可以將響應拆分為多個 JSP,在父 JSP 中包括其他 JSP。有兩種包含方式,靜態包含和動態包含:

            • 靜態 JSP 包含在編譯時解析。JSP 編譯器會包含所引用的文件,而不包含 include 語句。此選項通常非常快,完全不會增加運行時開銷。

              																						
              																								<%@ include file="include2.jsp" %>
              																						
              																				

            • 動態 JSP 包含在運行時解析,開銷并不小。就垃圾生成和執行時間而言,解析要調度的正確 JSP 開銷非常大。例如(在 JSP 中):

              																						
              																								<jsp:include page="include2.jsp" flush="true" %>
              																						
              																				

              JSP 中的動態包含在通過 Servlet 代碼包含其他文件時使用如下語句:

              																						
              																								javax.servlet.RequestDispatcher 
              																						
              																				

              因此,只要有可能,應該盡量使用靜態包含。動態包含提供了最高的靈活性,但如果使用過于頻繁,會帶來巨大的性能開銷。

          EJB 用法

          Enterprise JavaBean (EJB) 定義了一個基于組件的體系結構,用于構建可擴展的分布式多用戶業務應用程序。EJB 組件設計用于封裝業務邏輯,并同時將所有的復雜性隱藏在 Bean 和內置 EJB 容器服務后。

          對企業應用程序頻繁使用的各種功能的支持會帶來一定的性能開銷,在使用 EJB 時需要加以考慮。

          • Portlet 可以通過 JNDI 查詢包含 EJB 引用,而 JNDI 查詢在性能方面開銷很大。例如,如果 Portlet 并不緩存對 EJB 主接口的引用,則每個對 EJB 的邏輯引用需要兩次遠程調用:一個調用命名服務,另一個調用實際的對象。為了改進這種情況,請使用緩存技術以減少或消除對 EJB 主引用的重復查詢。

          • EJB 組件將公開遠程接口和本地接口。依賴于位置的 EJB 將使用遠程接口。方法參數和返回值將在 RMI-IIOP 上序列化,并由值返回。遠程方法必須設計為能夠根據 API 的使用模式滿足數據需求。請使用 API 中的適合接口的使用情況的方法和數據類型粒度,以盡可能減少序列化開銷。

          • 盡可能減少遠程調用的數量,以減少由于代碼路徑中的遠程調用帶來的系統開銷。使用會話 Bean 作為遠程外觀使用,對復雜交互進行包裝,并減少 Portlet 和域對象間的遠程調用。直接訪問遠程實體 Bean 的 Portlet 通常會導致多個遠程方法調用。如果在此環境中使用實體 Bean,請避免給予其遠程接口。作為外觀的會話 Bean 將通過其本地接口訪問實體 Bean,從其收集數據,然后將此信息返回發出調用的應用程序。

            當發出調用的客戶機(如會話外觀)與被調用的 EJB 共享同一個容器時,本地接口的概念將會有效果。使用本地接口可以消除分布式對象協議的系統開銷,從而降低進程間通信開銷。本地調用并不會通過通信層,所有對象均可以通過引用傳遞。

          • EJB 容器支持的事務管理也可以影響性能。開發了 EJB 后,程序員必須設置定義各種特征(如 EJB 的事務支持和隔離級別)的部署描述符。如果不需要事務,請將事務類型設置為 NotSupported。

          • 事務隔離級別是基礎數據庫將已更改但尚未提交的數據向其他事務公開的程度。為了獲得最佳的性能,請使用自由隔離級別。不過,讓其事務看到未提交的數據可以帶來意料之外的副作用,如更新沖突和讀取不一致等。有關如何設置隔離級別的說明,請參閱 WebSphere Application Server V5.1.x 信息中心

          請參閱 IBM 白皮書 WebSphere Application Server Development 性能和擴展性最佳實踐和 IBM 紅皮書 IBM WebSphere V5.1 性能、擴展性和高可用性 WebSphere 手冊系列,以獲得其他建議以及關于每個建議的相關理由。

          標記大小

          標記大小指從門戶服務器傳輸到客戶機的完全呈現門戶頁面的字節數量。從門戶服務器的角度來看,最重要的部分是包含結果標記的 HTML 頁面的大小。也必須將其他文件(如樣式表、圖像或 JavaScript)傳輸到客戶機。由于靜態文件通常保存在 HTTP 服務器或代理緩存上的門戶系統之外,所以,此處我們將主要討論“真正的”HTML 標記大小。

          到底出于什么原因非得關注標記大小呢?在公司的內部網內,網絡帶寬的問題可能會少一些,但如果用戶通過調制解調器或其他低帶寬網絡連接到門戶,大型 HTML 響應很長的下載時間可能會令人非常受不了。

          讓我們進行一個簡單的計算。假設服務器或集群每秒鐘最多能處理 100 個請求。HTML 頁面大小應該為 100KB,這個值雖然看著很大,但如果在頁面上有復雜的主題和若干個 Portlet,就很容易達到這個大小。對于服務器,這意味著必須提供約 10MB/sec 的速度(100 KB * 100 頁面/秒)。而這個值是 100MB 的網絡可以處理的最大通信流量。(以太網不可能百分之百地支持其 100MB/sec 的速度,且傳入通信流量也不容忽視。對于通過 56K 調制解調器連接到門戶的用戶,每個頁面的下載時間應在 15 秒的時間范圍內!)

          多大能稱為太大?這個問題通常很難回答。不過,每個 HTML 頁面的大小超過 100KB 可能就太大了。另外,還要記住,較小的設備對其可以處理的每個請求的標記大小有一定的限制。

          構成標記大小的主要內容是主題和 Portlet 輸出。由于所有門戶 JSP 均可自定義,所以可以改變標記的在終端的緊湊程度。要限制標記大小,可以采取以下措施:

          • 在 JSP 中使用 JSP 注釋,而不是 HTML 注釋。

            JSP 編譯器將刪除窗體注釋 <%-- ... --%>,而保留窗體注釋 <!-- ... -->并將其通過網絡傳輸。

          • 盡量減少 JSP 源文件中的空白、制表符和分行符,因為 JSP 編譯器將會保留這些內容。

            這可能會降低代碼的可讀性。這些內容可以幫助開發布局良好的代碼,但在 JSP 文件應用到生產環境前,將使用工具對其進行處理,除去其格式設置。

          • 盡量避免多次向客戶機發送相同的信息。

            例如,樣式定義應當放入獨立的 CSS 文件中。JavaScript 代碼也應如此。而且,由于這些獨立的文件通常不會更改,因此可以將其緩存在瀏覽器或代理緩存中,從而進一步減少網絡通信流量。

          • 如果您的環境設置為支持壓縮,還可以使用 HTTP 壓縮將壓縮過的標記發送到的客戶機。

            請參考 Web 服務器和客戶機的文檔,以獲得詳細信息。

          日志記錄、跟蹤和 I/O

          日志通常最終會涉及到對硬盤寫入。從性能的角度而言,任何與磁盤頻繁進行交互的內容都是潛在的大開銷操作,因此,最好盡量減少在生產環境中使用 Java I/O 庫。由于通常通過使用某些 Java 編程之下的本機庫提供 I/O,因此會有一定的缺省系統開銷。System.out.println 之類的操作在文件 I/O 期間會對處理進行同步,這將對性能造成很大的影響。

          在開發和測試模式中,可能希望所有日志記錄和調試功能均為活動狀態,因為這些功能對于發現錯誤非常重要。在生產環境中部署應用程序時,讓各種日志功能均處于打開狀態并非可行的選擇。最佳實踐應是對日志語句加以保護,使其僅在出錯和進行調試的情況下打開。可以通過使用一個最終的 Boolean 變量實現此功能,當將其值設置為 false 時,可以有效地指示編譯器進行優化,不再檢查和執行日志記錄代碼:

          														
          																static final boolean LOGGING = false;
          if (LOGGING) {...}
          														
          												

          Java 語言提供了兩種流:讀取器/寫入器和輸入/輸出:

          • 讀取器和寫入器是在 I/O 操作中支持 unicode 字符的高級接口。
          • 輸入/輸出流提供非常低的級別(字節級)的數據訪問機制。

          讀取器/寫入器有性能開銷,因為它們旨在用于字符流,且會在后臺將數據編碼為字節。只要希望操作二進制數據,就應該使用輸入/輸出流。

          為了盡可能提高 I/O 性能,應該對讀取和寫入操作進行緩存。如果希望寫入大量來自 Portlet 的數據,通常最好采用對已緩存的數據進行部分刷新的方式,而不采用對全部數據一次性刷新的方式。另一方面,不要太頻繁地刷新緩沖區。

          同步與多線程

          用于協調對共享對象的訪問的 Java 機制稱為同步。同步語句一次僅允許一個線程進入代碼塊。

          • 在 Portlet 的生存期中,容器會將不同線程中的服務請求發送到單個 Portlet 實例。請避免在 Portlet 中進行同步,因為同步有很大的性能影響:同步會減少并發,因為在同步塊中一次僅允許運行一個線程,所有并發的線程都要進行排隊。另外,Java 虛擬機會使用監視器以支持同步,管理這些監視器也有性能開銷。除了性能影響之外,還可能出現死鎖,而這可能導致單個 Portlet 凍結,或者甚至更糟,導致整個門戶凍結。由于監視器不支持進行任何死鎖處理,因此程序員應負責防止死鎖的出現。

          • 在有必要進行同步的場合下,應該盡量縮小同步代碼塊。準確地識別哪些代碼真正需要同步并盡可能少地進行同步,這非常重要。如果同步代碼塊不夠小,應該對代碼進行分析,對其重構,以使所有可以異步運行的代碼均位于同步代碼塊之外。

          • 某些 Java J2SE 功能會間接地使用同步。Java 集合類(如 Vector 或 Hashtable)都是全面同步的。即使在單線程環境中 Java 程序也會有與線程同步相關的開銷。Java 1.2 引入的較新的集合(如 ArrayList)并不進行同步。這就提供了對數據更快的訪問。在需要線程安全的情況下,請使用線程安全視圖。線程安全視圖是包裝類,該類增加了同步標準集合方法的功能。集合類的工廠方法將返回線程安全的集合,該集合由特定的集合類型的實例支持:

            																		
            																				List list = Collections.sychronizedList(new ArrayList());
            																		
            																

          • 另一個非直接同步的例子就是 Java I/O 庫。請盡可能少地使用 Java I/O 庫方法(例如 System.out.println()),以減少不必要的性能開銷。

          • 不要從 Portlet 生成非托管線程。當前 J2EE 強烈建議不要試圖在容器生成新線程。實際上,J2EE 規范 6.2.1 編程限制指出:

            “如果應用程序組件包含的功能與 J2EE 系統基礎結構所提供的功能相同,則會存在功能沖突和管理混亂。例如,……以管理線程……”

            不要試圖生成新線程的一個實際原因是因為新線程對 J2EE 上下文沒有完全訪問權限。而且,新創建的非托管線程會妨礙 WebSphere Portal 實現穩定的、優化的可擴展運行時環境。因此,請使用 WebSphere Application Server 中的異步 Bean 功能(請參閱 WebSphere Application Server Enterprise V5 和編程模型擴展 WebSphere 手冊系列)。異步 Bean 是一個 Java 對象或 Enterprise Bean,能夠使用 J2EE 上下文提交在獨立線程(異步)運行的代碼。

          Portlet

          Portlet 編程模型允許開發人員創建特定類型的 Web 應用程序,此類應用程序可以作為客戶機瀏覽器中若干此類應用程序的聚合視圖的一部分。在 WebSphere Portal 中,此類應用程序不僅能共存在一個頁面(即聚合視圖)上,還能在構造該頁面時彼此進行通信。因此,Portlet 的實現可以影響頁面的總體性能;例如,如果特定的“關鍵”Portlet 駐留在頁面上,則值得花精力在同一個頁面上實現一些其他的關鍵性能 Portlet。

          后端訪問

          在實際的門戶中,完全自我依賴的 Portlet 非常少見,因為門戶通常用作網站的附加內容或幫助工具。此類 Portlet 應僅在其本地代碼執行路徑中優化,不應對允許的門戶系統帶來太多的負荷。

          Portlet 更為典型的用戶就是提供需要訪問其他數據源或事務系統的應用程序功能,除了 Portlet 的原始執行系統之外,這些數據源或事務系統也需要執行資源。數據可能會從網絡上的其他后端系統檢索或存儲到其中。需要在總體系統設計中考慮在后端系統上可能出現的事務長度、隔離級別以及數據鎖定。

          請注意,單個 Portlet 可能不是后端系統的唯一客戶機。事實上,在實際使用中,會有很多客戶機連接到此類系統,甚至單個 Portlet 還可能同時多次訪問同一個后端系統。Portlet 可能會在多個獨立的服務器線程中執行其代碼以響應不同的用戶請求。因此,有必要對訪問模式進行了解,Portlet 或其他客戶機獲取事務或鎖定的方式可能會影響此類后端系統的平均響應時間。

          如果某個 Portlet 在操作或呈現階段需要進行密集的后端系統訪問,響應時間(完成這些階段的時間)將越來越依賴于后端系統的響應。(如果等待門戶服務器外的響應以滿足傳入請求,將會帶來延遲,此延遲不能通過優化 Portlet 代碼的執行路徑得到改善。)具有后端系統通信的良好設計,并了解事務行為通常可以得到更高的性能。

          為了避免由于后端系統崩潰而使 Portlet(以及其所在頁面)停止響應,可以在代碼中加入超時機制;不過,請注意,管理和跟蹤時間戳會帶來一些處理開銷。如果使用了 WebSphere Portal 中的并行 Porlet 呈現功能(稍后討論),則可為并行呈現線程配置超時。

          盡可能減少與此類外部后端系統的交互和數據通信流量也是不錯的做法。為了實現這一點,如果信息的刷新標準允許進行緩存,Portlet 可以對信息進行緩存。這可以減少為每個傳入 WebSphere Portal 請求多次獲取相同數據的往返次數。這樣還可以幫助降低后端系統上的負載,因為這樣就無需多次提供相同的信息了。另外,如果不需要在網絡上傳輸數據,Portlet 可能可以更快地進行呈現。

          避免到后端系統的往返的另一個方法就是除了檢索滿足當前請求實際所需的數據外,還檢索所知的將在可能的后續請求中所需的數據。不過,使用此方法時,如果知道在后續請求中將要實際需要哪些預提取數據,我們仍然建議使用普通的預提取功能。為了合理地設計此特性,需要對 Porlet 應用程序的典型用戶交互非常了解。要記住,提前檢索會對門戶 JVM 的內存使用造成影響。(請參閱性能和可伸縮性代碼設計。)此類設計方法可能需要更改后端系統的接口,但可以節約大量的處理時間,使得更改物超所值。

          對于緩存,WebSphere Application Server 利用其面向的 Portlet 的 DistributedMap 接口提供了動態緩存功能。(請參閱 WebSphere Application Server 5.1 信息中心以獲得更多的信息。)

          會話與其他數據存儲區

          保持和維護 Portlet 的數據,使其生存期長于單個請求的生存期,這是一個典型的 Portlet 編程任務。通常考慮采用的第一個方法就是使用 PortletSession。從程序員的角度而言,PortletSession 使用很方便,但從應用程序服務器的角度而言,管理會話需要使用資源。如果會話包含越來越多的數據,從而要求使用更多的內存,則會進一步使問題嚴重化。

          如果將會話配置為持久地存儲在數據庫中,或配置為進行內存到內存復制(即在集群化環境中為 WebSphere Portal 配置了故障轉移),則該會話將在其內容更改時被序列化。

          當會話數據寫入到遠程副本時,對會話數據進行序列化和反序列化所需的時間可能變得非常大。在非常少見的情況下,存儲在會話中的某些對象可能被標記為瞬態的。這將降低會話的序列化后的大小,但不會更改內存的大小,而這對應用程序服務器處理會話的效率也有影響。

          大型的會話對象會減少可用以創建和執行應用程序對象的 JVM 內存。因此,隨著可用堆內存的減少而導致更頻繁的垃圾回收,性能可能會降低。

          另一個因素就是內存內的生存期比所需的使用時間長,因此占用 Java 堆中的空間的會話數量通常比活動用戶的數量多。在 WebSphere Application Server 中可以配置會話過期時間,這個屬性非常必要,可以防止在幾秒鐘沒有活動后就要求用戶再次登錄的情況。會話的釋放由 WebSphere Application Server 和 Portlet 容器負責。

          序列化的會話大小應該小于 4KB,因為 WebSphere Application Server 能以可以接受的數據庫性能開銷存儲此類會話,在網絡上傳輸此類會話的時間也更少。如果會話大小超過了 32KB,數據庫必須使用面向二進制大對象配置的表單元格,而如果此類會話從數據庫檢索或寫入到數據庫中,則將需要訪問物理磁盤(對于大多數受支持的數據庫)。

          由以上分析得出的第一個結論就是,從應用程序的角度而言,應該盡可能避免創建會話。在大多數公共頁面和無需身份驗證的頁面上,通常不需要會話。在此類頁面上可以通過呈現鏈接與門戶進行交互,而呈現鏈接定義為不更改服務器端的狀態。門戶將為每個 Portlet 維護呈現參數,以用于對該頁面的所有后續請求。為了避免 JSP 缺省創建會話,應該將 JSP 中的頁面會話指令設置為 false:

          														
          																<@ page session="false"%>
          														
          												

          否則,如果不存在會話,此 JSP 將創建一個會話。

          以下的 Java 代碼片段演示了如何確保傳入會話加入現有的會話,而不是無條件地創建新會話:

          														
          																PortletRequest.getPortletSession(false)
          														
          												

          將此參數的值設置為 false 時,如果之前不存在會話,將不會創建會話。如果之前不存在會話,僅為了在其中存儲數據而在 Portlet 中創建一個會話,可能并不合適。

          由以上分析得到的第二個結論就是,要將會話誤用作通用數據存儲機制。請記住,我們的目的是盡可能使會話保持最小。如果由于 Portlet 的設計,將某些數據保存在內存中具有一定優勢,則可以使用緩存。可以使用會話 ID 設置緩存條目的范圍,以使會話和要保存在內存中的數據建立關聯。請注意,此類緩存在呈現故障轉移時不支持集群;而這有時是可以接受的折衷。如果數據可以使用其他 Portlet 可用數據重新創建,則緩存條目的會話范圍要求就有待商榷。

          在很多情況下,通過僅在會話中存儲一個鍵,并使用該鍵作為引用以在其他數據結構中查找更大的對象,從而可以避免在會話中存儲大對象。另外,可以選擇使用相同信息的更緊湊的表示形式,而后將該對象放入會話中。

          而且,Portlet 設計需要仔細考慮會話中實際存儲的內容。會話通常僅旨在用于存儲用戶交互與門戶應用程序的對話狀態(例如,網上商店 Porltet 中的購物車的內容)。此類數據不能采用其他任何手段重新創建。在 WebSphere Portal 中,這種類型的數據處理稱為會話狀態

          如果并不需要會話狀態,Portlet 可以使用其他數據存儲選項:

          • 在 Portlet 的操作階段,可以為 Portlet 的后續呈現階段設置呈現參數。Portlet 使用呈現參數呈現其特定于一組特定值的視圖。由容器在請求間維護呈現參數,即使出現與其他 Portlet 的交互也是如此。在 WebSphere Portal 中,這種類型的數據處理稱為導航狀態

          • 如果需要跨多個用戶會話保持數據,則可以使用 PortletPreferences API 為 Portlet 存儲數據。請記住,此 API 并不能替代通用數據庫。在 WebSphere Portal 中,這種類型的數據處理稱為持久性狀態

          • PortletConfig API 使 Portlet 可以讀取其配置,該配置由開發人員通過使用 Portlet 部署描述符提供;這對于 Porltet 的所有用戶均有效。

          • PortletContext API 允許存儲同一應用程序中其他 Portlet 也可以訪問的屬性。

          請考慮使用會話之外的其他選擇,將其用于存儲 Portlet 創建和使用的數據。避免將可以通過用戶交互之外的其他源重新創建的數據復制到會話中。

          呈現鏈接與操作鏈接

          與對特定的 Portlet 視圖尋址相比,使用呈現參數有很多優勢。

          如果 WebSphere Portal 檢測到了 Portlet 的操作參數,則必須調用特殊的操作階段處理,使其具有不必使用操作參數的優勢。不過,請注意,處理呈現鏈接時一定不能更改 Portlet 的服務器端狀態。要更改服務器端狀態,唯一得到認可的方法就是使用操作鏈接,而對于事務類型的請求,操作鏈接是最好的選擇。

          使用呈現鏈接而不使用操作鏈接的例子很多。例如,假設一個報紙 Portlet 可以同使用“上一頁”和“下一頁”按鈕顯示特定的頁面。逐頁瀏覽報紙的頁面不一定會更改服務器端的狀態,此狀態在本例中就是報紙中包含的全部信息。為了尋址報紙的下一頁,將下一頁的的頁碼編碼到所顯示按鈕的呈現鏈接中就足夠了。Portlet 可以根據呈現參數中所給的頁碼確定要呈現的頁面。

          此外,由于每個呈現的視圖都由獨立的 URL 尋址,所以,通過使用呈現鏈接而不使用操作鏈接,還可以充分利用緩存基礎結構(無論是瀏覽器緩存還是代理緩存)。URL 是用于訪問此類緩存基礎結構中的特定生成視圖的唯一的鍵。

          Portlet 功能

          接下來的幾個部分中將討論開發人員應該考慮的 WebSphere Portal 中可用的一些 Porlet 優化功能,這些功能可以影響所選擇的實現技術。需要使用 Portlet 的部署描述符提供一些必須的設置,而且,由于這些項也是由 Portlet 開發人員提供的,因此被認為是自定義代碼。

          允許 Portlet 進行并行呈現

          WebSphere Portal 提供了讓頁面上的 Portlet 并行呈現的選項。此功能并非完全“免費”的,因為需要計算資源以維護和管理呈現每個 Portlet 所使用的不同線程。

          如果涉及到很多后端系統,而每個后端系統在呈現單個頁面時都會產生延遲,此時使用并行 Portlet 呈現就具有一定優勢。例如,假設一個門戶頁面包含很多 Portlet,每個 Portlet 都會訪問不同的后端系統。在串行呈現模式中,從所有后端系統檢索所需數據的總體延遲為各個延遲時間的總和。而在并行呈現模式中,延遲時間應為所有單個延遲時間中的最大值。

          如果 Portlet 并不經常使用后端系統,由于啟用并行 Portlet 呈現所帶來的開銷可能會比由此功能所帶來的好處更大。如果頁面上的 Portlet 能夠獨立于后端系統進行呈現,則只需要門戶服務器計算機本地的 CPU 資源。這種情況下,頁面呈現響應時間不會得到改進。

          可以使用圖形用戶界面、部署描述符或 WebSphere Portal 的 XML 訪問接口啟用并行 Portlet 呈現。而且,還有一個相關的全局屬性值,可以全面開啟和關閉并行 Portlet 呈現功能。

          要正確回答是否支持并行 Portlet 呈現門戶這一問題,需要考慮若干事項;例如,呈現頁面所涉及到的后端系統的數量、使用并行 Portlet 呈現的頁面上的 Portlet 的平均數量,等等。Portlet 開發人員事先不一定能給出這些問題答案,但如果合理的話,開發人員當然事先可以確保為 Portlet 啟用了并行 Portlet 呈現。

          在 Portlet 容器中進行緩存

          基于 Portlet 的 Web 頁面是動態聚合的,因為它們能以個性化的方式提供動態內容。這個靈活性具有一定的開銷。由于為了響應請求生成這些頁面必須進行額外的工作,故而網站的響應時間將增加。

          新的緩存技術將改善動態頁面的生成和減少系統負載。WebSphere Portal 支持片斷緩存(也稱為 Servlet 緩存),可以使用 WebSphere Application Server 動態緩存在緩存中保存 Portlet 輸出。對緩存的 Portlet 的請求將從緩存(而不是 Portlet)檢索內容。可以通過在部署描述符中指定過期實現片斷緩存的失效。而且,在 Portlet 的操作階段也會使片斷緩存條目失效。

          激活片斷緩存不需要進行費時的安裝和集成工作。通過使用簡單的 XML 部署描述符文件和通過使用 WebSphere Application Server 管理控制臺均可以啟用和禁用該緩存功能。(請參閱 WebSphere Portal 信息中心,以了解在 WebSphere Application Server 中啟用 Servlet 緩存的詳細信息。)

          為了使用基于過期的緩存,Portlet 必須在部署描述符 portlet.xml(對于符合 JSR 168 規范的標準化 Portlet)中定義過期緩存的持續時間:

          														
          																<expiration-cache>300</expiration-cache>
          														
          												

          • 整數定義緩存條目在緩存中存在的秒數值。

          • 值 -1 指示 Portlet 緩存永遠不過期。

          • 值 0 指示為該 Portlet 禁用緩存功能。

          一定不能在同一 Portlet 的所有用戶間共享緩存的條目。此緩存技術是基于特定 Portlet 的特定用戶的。

          對于在其部署描述符中定義了過期緩存的 JSR 168 Portlet,Portlet 窗口可以在運行時通過設置 RenderResponse 中的 EXPIRATION_CACHE 屬性修改過期時間,如下所示:

          														
          																renderResponse.setProperty(
             RenderResponse.EXPIRATION_CACHE,
             String.valueOf(numberCrunchingCalculation())
          );
          														
          												

          對于在從后端(如 EJB 組件和數據庫)計算其響應和請求數據時計算時間很長的復雜 Portlet,此方法非常有用。對于簡單 Portlet,不應啟用片斷緩存。WebSphere Portal 將使用額外的執行資源計算機片斷緩存的內部緩存鍵。對于簡單 Portlet,由于緩存鍵計算比重新計算 Portlet 響應開銷更大,其性能可能會降低。

          對于真正動態的 Portlet,片斷緩存并不適用;如,對每個請求都需要從其他數據源收集當前數據的基于實時的 Portlet 或對每個請求都會更改其響應標記的 Portlet。這將會導致大量的緩存失效,因此性能不會得到提高。所以,僅在 Portlet 的輸出在更新前會在一段時間內保持有效的情況下才應該為 Portlet 啟用緩存功能。

          在遠程緩存中進行緩存

          通過獨特的自適應緩存功能,WebSphere Portal 可以在門戶緩存之外的緩存(稱為遠程緩存)中動態緩存生成的頁面(如果所有頁面組件均指示自身可以緩存)。如果從遠程提供完全呈現的頁面,就可以避免到門戶服務器的往返,此類頁面的響應時間可以與從靜態網站提供時一樣快。

          有關遠程緩存的全部詳細信息,請參閱使用 WebSphere Portal V5.1 開發包含靜態內容和動態內容的高性能網站

          Portlet(以及主題)可以提供完全呈現頁面的總體遠程緩存信息中其所特定的遠程緩存信息。遠程緩存信息一個數據結構,由關于緩存范圍(是否可緩存,是共享的,還是非共享的)和過期時間(內容在多長時間內為有效)的信息組成。可以通過部署描述符或 WebSphere Portal GUI 提供 Portlet 的遠程緩存信息。除此之外,Portlet 還可以在呈現時為每個 Portlet 提供遠程緩存信息,如下面的代碼中所示:

          														
          																. . . 
          import com.ibm.wps.util.RemoteCacheInfo;
          import javax.portlet.RenderResponse;
          . . .
          /* Do rendering */
          public void doView(RenderRequest renderRequest, RenderResponse renderResponse)
             throws PortletException, IOException {
             /* Some code might happen here */
             . . . 
             /* Publish a dynamic expiration time during rendering */
             renderResponse.setProperty( 
                RenderResponse.EXPIRATION_CACHE, 
                String.valueOf(numberCrunchingCalculation())
             );
             /* Publish a cache scope value of "shared" during rendering *)
             renderResponse.setProperty( 
                RemoteCacheInfo.KEY_SCOPE, RemoteCacheInfo.Scope.SHARED_STRING );
             /* Some other code might happen here */
             . . . 
          }
          														
          												

          設置遠程緩存信息的方式依賴于呈現的視圖的“刷新”要求和范圍。請注意,如果從緩存提供呈現的頁面,請求可能甚至不會發送到門戶服務器。

          如果可以在基礎結構中使用緩存,自定義 Portlet 開發人員應當考慮利用遠程緩存功能。

          主題與外觀

          在門戶術語中,主題是確定門戶應用程序的外觀和風格的若干 JSP 集。由于主題由 JSP 組成,在 JSP 部分給出的技巧也適用于此。這一部分詳細討論了使用組成主題的 JSP 文件集可能存在的性能缺陷。

          通常,主題由很多不同的文件組成,每個文件提供屏幕的特定區域的內容。盡管可以動態地包含 JSP,但通常(也建議)將 JSP 靜態包含在其他 JSP 中。

          由于編譯時可能會將很多 JSP 包含到其他 JSP 中,所得的 Java 源代碼和 Servlet 字節代碼文件可能會非常大。使用大的類文件通常不會有性能問題,但由于 Java 編程語言中包含的大小限制,可能不能將 Java 源代碼編譯為類。例如,Java 中的方法的大小不能超過 64KB。大型的復雜主題很容易達到這個限制,而導致不再能編譯。這種情況下,有三種選擇:

          • 用動態包含代替一些(而非全部)靜態包含。

            如 JSP 部分 (LINK) 提到的,這是用性能作為交換,以便能編譯 JSP。從性能的角度而言,盡管這個方法最易于實現,但卻是最不好的解決方法。

          • 盡量限制 JSP 中 Scriptlet 的使用。

            WebSphere Application Server 可以對僅調用標記處理程序的代碼進行優化,而這可以有助于使文件保持在 64KB 的上限之內。

          • 清除 JSP 代碼。

            這些文件通常包含并非必要的多余代碼。通常刪除 HTML 注釋行或空白,或者將 JavaScript 代碼移動到單獨的文件中均可保證足夠的空間。

          主題有時會完成應用程序中復雜的任務。不過此時應該謹慎。請記住,對于門戶的每個請求,都會呈現主題,因此不要在其中進行會給系統帶來高負荷的計算工作。

          在模擬門戶功能時要特別謹慎。例如,主題可能會循環訪問門戶應用程序中的大量頁面;應該對此進行篩選,僅向用戶顯示一個導航結構,其中僅包含主題從門戶 API 請求的若干頁面。這種情況下,門戶中進行的很多處理都會丟失,因為之后會將其結果丟棄。此處根據門戶訪問控制或個性化規則進行篩選會更為有效。

          此外,要盡量限制門戶頁面中門戶資源鏈接的數量。門戶必須生成的每個 URL 鏈接都會給系統帶來額外的負載。如果需要具有大量鏈接的應用程序主題,請嘗試緩存其中的一些頁面,從而使其不必在每次請求時都重新計算所有鏈接。

          主題也是 WebSphere Portal 中的遠程緩存基礎結構的一部分。主題的遠程緩存是一組可以通過 XML 訪問具體指定的元數據,如以下示例中所示:

          														
          																<!-- Theme "shared" scope and 40 seconds cache expiration -->
          <theme action="update" active="true" objectid="xmplTheme" uniquename="wps.theme.example">  
             <parameter name="remote-cache-scope" type="string" update="set">SHARED</parameter> 
             <parameter name="remote-cache-expiry" type="string" update="set">40</parameter>     
          </theme>
          														
          												

          主題不能提供任何呈現時遠程緩存信息。

          WebSphere Portal 支持高性能外觀的識別。這些外觀非常特殊,因為它們不是基于 JSP 生成的;它們的輸出是根據預編譯的 Java 類創建的。當然,此類外觀的可自定義性要差一些;只能對樣式表信息和包含的圖像進行修改。不過,如果性能是您要考慮的最重要的因素的話,就應該考慮為頁面上特定的元素或特定 Portlet 啟用高性能外觀。(請參閱參考資料中的信息中心以了解詳細信息,包括各種可幫助您編寫高速外觀和主題的提示。)

          工具

          在 WebSphere Portal 應用程序開發和驗證的所有階段均可以使用各種工具提供幫助。本部分對不同開發周期中可以使用的不同工具類別進行了說明,并提供了一些例子,以幫助您進行自定義代碼的開發和分析。

          開發環境

          從技術角度而言,可以使用任何文本編輯器編寫 Portlet、主題和外觀,但使用集成開發環境(如將 IBM Rational? Application Developer 和 IBM Portal Toolkit 結合使用)要方便很多。還可以使用 Portlet 代碼示例和基本門戶代碼片斷開速入門;該開發環境還與一個門戶服務器進行了集成,以便立即部署和測試代碼。

          性能分析工具

          當代碼就緒,可以部署時,需要詳細了解其可能的性能問題。可以采取若干步驟(下面對此進行了總結),但性能方面有一條始終適用的一般規則:在大多數程序中,約有 80% 的執行時間都花在 20% 的應用程序代碼中。這 20% 的代碼位于“關鍵路徑”上,正是這些方面值得進行性能優化。例如,Portlet 的呈現方法要比其初始方法的性能關鍵性更強,因為每個請求都會調用呈現方法。

          • 代碼分析應在開發的早期階段進行,或將其作為開發后的第一個性能測試。分析意味著將在方法級收集執行時信息,通常會使用 JVMPI 接口進行此項工作。分析器結果可以幫助標識應用程序的關鍵路徑;即大部分時間所執行的代碼。分析器還通常會給出關于對象創建速率和內存使用的信息。

          • 一旦將 Portlet 部署到了門戶中,就應該測試 Portlet 在負載下的行為。壓力或負載生成器(如 Rational Performance Tester、Rational Robot、Apache JMeter 等等)是具有成本效益的負載測試解決方案,可以幫助您準確地模擬生產負載下的系統性能。這些工具將收集大量信息,以幫助確定系統是否具有良好的性能設計,其中包括關于請求響應時間、處理器使用率等的數據。

          • 在負載測試期間,應該監視門戶環境中的若干性能參數。IBM Tivoli? Performance Viewer(與 WebSphere Application Server 一起提供)可以幫助監視應用程序服務器內的資源使用情況。

          • 門戶環境的許多問題都和內存有關。JVM 實現為工具提供了兩類信息,以供進行性能分析:

            • 垃圾回收器的輸出 verbose:gc。
            • 堆轉儲,發現內存泄漏時非常有用。

            在 IBM alphaWorks 中可以得到垃圾回收器輸出的分析工具。而另一方面,heapRoots 則是一款強大的堆轉儲分析輔助工具。《IBM Java 診斷指南》也提供了處理門戶的相關性能問題的有用信息。請參閱參考資料,以獲得這些參考資料的鏈接。

          開發 WebSphere Portal 代碼時,通常不需要所有這些工具,但要在生產環境中推出更大的門戶,必須從性能的角度對門戶代碼有個良好的理解。

          結束語

          創建自定義門戶代碼時,開發人員必須考慮很多方面的因素,以確保門戶性能得到優化。小結如下:

          • 將精力主要放在關鍵代碼路徑的改進上。關鍵代碼路徑是處理時間長或頻繁執行的代碼路徑。找到哪些類的哪些方法位于關鍵路徑上。在關鍵路徑外的優化效果相當小。

          • 要同時兼顧執行性能和內存分配。

          • 使用恰當的工具測量和分析代碼,以獲得最典型的用戶交互。

          • 不同編碼問題解決方案可能有很大的性能變化。

          • 必須全面了解處理發現的性能問題的特定實現的細節。

          • 設計自定義代碼時要考慮后端訪問模式。

          • 不要錯誤地將會話作為 Portlet 的通用數據存儲區使用。可以采用更好地方法處理數據,以滿足各種不同的實現要求。

          • 考慮利用 WebSphere Application Server 和 WebSphere Portal 提供的特殊功能以優化 Portlet 性能(假設目標環境也在使用相同的功能)。

          posted @ 2006-04-04 12:44 劉軍偉 閱讀(669) | 評論 (0)編輯 收藏

          僅列出標題  
          主站蜘蛛池模板: 清新县| 察哈| 读书| 阳春市| 泰安市| 滨海县| 邵武市| 陆河县| 红原县| 罗山县| 桂平市| 什邡市| 天门市| 濮阳市| 梅河口市| 泽州县| 元朗区| 潼南县| 新郑市| 南皮县| 喜德县| 五原县| 犍为县| 瓦房店市| 庆城县| 利辛县| 达州市| 海兴县| 宜阳县| 错那县| 务川| 揭西县| 托克逊县| 繁昌县| 巴东县| 凯里市| 荔波县| 麻栗坡县| 子洲县| 四平市| 舒城县|