前 言 隨著近來J2EE軟件廣泛地應用于各行各業,系統調優也越來越引起軟件開發者和應用服務器提供商的重視。而對于最終客戶來說,在一個高效、穩定地實現他們的業務需求已經是他們的基本要求。所以J2EE調優顯得非常重要,而BEA WebLogic Server是業界領先的應用服務器,BEA WebLogic平臺下的J2EE調優也就尤為重要,她將為我們提供普遍的J2EE調優方案。最近網絡、雜志上的J2EE調優文章層出不窮。本人也將自己平時工作中的一些經驗積累分享給大家,拋磚引玉。 本文從J2EE應用架構(下圖)來分別剖析系統調優,首先我們一般會從應用程序出發,去審核代碼,做到代碼級的優化,然后再調整應用服務器(BEA WebLogic8.1)和數據庫 (Oracle9i)的參數,最后當然是調整操作系統和網絡的性能(包括硬件升級)。誠然,在我遇到的很多項目中,都是出現了性能問題后才想到調優,而且一般都是先進行系統參數調整,實在解決不了才會對代碼進行檢查.實際上,我們應當將代碼級的調優放在應用設計時來做,測試生產時修改代碼將是一件極其痛苦的事情。 第一章 應用程序調優 1.1.1 通用代碼調優 1.1.2 減小沒有必要的操作 對象的創建是個很昂貴的工作,所以我們應當盡量減少對象的創建,在需要的時候聲明它,初 始化它,不要重復初始化一個對象,盡量能做到再使用,而用完后置null有利于垃圾收集。讓類實現Cloneable接口,同時采用工廠模式,將減少類的創建,每次都是通過clone()方法來獲得對象。另外使用接口也能減少類的創建。對于成員變量的初始化也應盡量避免, 特別是在一個類派生另一個類時。 異常拋出對性能不利。拋出異常首先要創建一個新的對象。Throwable接口的構造函數調用名為, fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,VM就必須調整調用堆棧,因為在處理過程中創建了一個新的對象。 異常只能用于錯誤處理,不應該用來控制程序流程。 此外, 建議關閉Debug輸出,盡量少用串行化、同步操作和耗時昂貴的服務(如Date())。 1.1.3 使用合適的類型 當原始類型不能滿足我們要求時,使用復雜類型。String和StringBuffer的區別自不必說了,是我們使用最多的類型,在涉及到字符運算時,強烈建議使用StringBuffer。在做String匹配時使用intern()代替equal()。 帶有final修飾符的類是不可派生的, 如果指定一個類為final,則該類所有的方法都是final。 Java編譯器會尋找機會內聯所有的final方法,這將能夠使性能平均提高50%。類的屬性和方式使用final或者static修飾符也是有好處的。 調用方法時傳遞的參數以及在調用中創建的臨時變量都保存在棧(Stack)中,速度較快。所以盡量使用局部變量。 ArrayList和Vector,HashMap和Hashtable是我們經常用到的類,前者不支持同步,后者支持同步,前者性能更好,大多數情況下選擇前者。 1.1.4 盡量使用pool,buffer和cache 使用pool、buffer和cache能大大提高系統的性能,這在J2EE的大部分技術中都是適用的。 在WebLogic中就大量使用了池:JDBC Connection Pool、Socket Pool、Object Pool和Thread Pool。I/O操作中,buffer是必須的,特別是對大文件的操作,不然容易造成內存溢出。字節操作最快,所以盡可能采用write(byte[]),Buffered FileOutputStream比Buffered FileWriter要快,因為FileWriter需要Unicode到Byte的轉換。 而后面講到的JDBC、JSP、EJB和JMS我們都非常建議使用buffer和cache。為HttpServletResponse設置buffersize,使用wl-cache,緩存在JNDI樹上獲取的對象等等。 此外,使用JDK 1.4的非阻塞I/O對性能也有很大提高。 1.2 JDBC代碼調優 1.2.1 嚴格資源使用 JDBC代碼調優最大的原則就是使用WebLogic的連接池,而不是自己直連數據庫。在我接觸的很多自己實現連接池的項目中,大部分遇到死鎖和連接泄漏的問題,最后得不得修改代碼。而WebLogic提供了功能強大,性能良好的數據庫連接池,我們要做的只是封裝一個連接管理類,從JNDI樹上獲取數據源并緩存,得到連接,并提供一系列關閉數據庫資源的方法。 對任何資源使用的原則是用完即關,不管是數據庫資源、上下文環境,還是文件。數據庫資源的泄漏極易造成內存泄漏,乃至系統崩潰。在使用完數據庫資源后依次關閉ResultSet,Statement和Connection,而在一個數據庫連接多次進行數據庫操作時要特別注意ResultSet和Statement依次關閉。
try{ //open connection pstmt =conn.prepareStatement(strSql1); pstmt.executeUpdate(); pstmt.close(); pstmt =conn.prepareStatement(strSql2); rs=pstmt.executeQuery(); while (rs.next()){ //process } rs.close(); pstmt.close(); }catch(Exception e){ //close rs,psmt,con }finally{ //close rs,psmt,con } 1.2.2 實用技巧 在JDBC操作中還有一些小的技巧跟大家分享:由于獲取連接時默認自動提交方式,使用connection.setAutoCommit(false)關閉自動提交,使用PreparedStatement,批量更新,業務復雜或者大數據量操作時使用存儲過程,盡量使用RowSet,此外設置記錄集讀取緩存FetchSize和設置記錄集讀取方向FetchDirection對性能也有一定的提高。 1.2.3 優化SQL語句 SQL語句的優化牽涉到很多數據庫的知識,需要與索引配合,因此需要DBA對代碼中的SQL進行檢查測試。常見的,select *不提倡使用,效率極差,建議顯式獲取列,即使是所有字段也應羅列,而取總數時使用count(*),為提高cache的命中率,盡量做到SQL重用。對于大數據量的查詢,可以充分利用Oracle數據庫的特性,每次取出m-n行的數據,實現分頁查詢。另外,提高性能的好選擇可能就是把所有的字符數據都保存為Unicode,Java以Unicode形式處理所有數據,因此,數據庫驅動程序不必再執行轉換過程。 1.3 Web代碼調優 1.3.1 HttpSession的使用 應用服務器保存很多會話時,容易造成內存不足,所以盡量減少session的使用,放置session 里的對象不應該是大對象,最好是簡單小對象,實現串行化接口。當會話不再需要時,應當及時調用invalidate()方法清除會話。而當某個變量不需要時,及時調用removeAttribute()方法清除變量。請勿將EJB對象放置在session中。 1.3.2 JSP代碼調優 目前,在JSP頁面中引入外部資源的方法主要有兩種:include指令,以及include動作。 include指令:例如<%@ include file="copyright.html" %>,該指令在編譯時引入指定的資源。在編譯之前,帶有include指令的頁面和指定的資源被合并成一個文件。被引用的外部資源在編譯時就確定,比運行時才確定資源更高效。 include動作:例如<jsp:include page="copyright.jsp" />。該動作引入指定頁面執行后生成的結果。由于它在運行時完成,因此對輸出結果的控制更加靈活。但是,只有當被引用的內容頻繁地改變時,或者在對主頁面的請求沒有出現之前,被引用的頁面無法確定時,使用include動作才合算。 對于那些無需跟蹤會話狀態的jsp,關閉自動創建的會話可以節省一些資源。使用如下page指令: <%@ page session="false"%> ;盡量不要將JSP頁面定義為單線程,應設置為<%@page isThreadSafe=”true”%>;在JSP頁面最好使用輸出緩存功能,如: <%@page buffer="32kb"%>;盡量用wl:cache定制標記來緩存靜態或相對靜態的內容,緩存jsp:include操作的結果能顯著提高應用程序的運行性能。 1.3.3 Servlet代碼調優 Servlet代碼調優比較簡單:在Servlet之間跳轉時,forward比sendRedirect更有效;設置HttpServletResponse 緩沖區,如:response.setBufferSize(20000);在init()方法里緩存靜態數據,而在destroy()中釋放它;建議在Servlet里使用ServletOutputStream輸出圖片等對象;避免在Servlet和Jsp中定界事務等。 1.4 JMS代碼調優 1.4.1 注意必要的事項,避免使用不必要的特征 JMS提供了強有力的消息處理機制,但是為了最大限度的提高JMS系統的性能,應避免使用不需要使用的特征,同時也要注意必要的事項。比如:盡量使用接收程序能直接使用的最簡單、最小的消息類型;消息選擇器要盡可能簡單(最好不使用),盡量不要使用復雜的操作符,如like、in或者between等,使用字符串數據類型的速度最慢;務必為特定的應用程序定義特定的JMS連接工廠,并且禁用默認的JMS連接工廠;不要在javax.*與weblogic.*的名字空間中使用JNDI名稱;盡量使用異步消費者,線程不必封鎖以等待消息的到達;使用完JNDI樹上的資源后注意關閉。 1.4.2 消息類型的選擇 標準JMS提供了五種消息類型,而TextMessage應用最為普遍, 當發送的消息是幾種原始數據類型的集合體時,最好使用MapMessage消息類型,而不要使用ObjectMessage,以便減少不同系統間的耦合。此外消息是否使用壓縮要慎重考慮,壓縮未必能減少消息大小。如果生產者、消費者和目的地并置在同一WebLogic Server內部,通常不使用壓縮。WebLogic特有的XMLMessage能為運行于消息主體之上的消息選擇器提供內嵌式支持,而且易于數據交換。因此,建議應用程序之間傳送消息使用XML消息格式,而應用程序內部間傳送消息使用二進制消息格式。 1.4.3 確認方式的選擇和JMS事務 使用事務性會話時,盡量使用恰當的消息確認方式:如果需求允許,使用NO_ACKKNOWLEDGE;非持久的訂閱者使用DUPS_OK_ACKNOWLEDGE或者MULTICAST_NO_ACKNOWLEDGE。而使用JTA的UserTransaction,確認方式將被忽略。在使用JMS事務時,無效的消息會導致事務的回滾,以致消息重發這樣的死循環。此時,可以將無效消息發送到錯誤消息隊列,并提交JMS事務,這將確保消息不會再次傳遞。 1.5 EJB代碼調優 1.5.1 有效使用設計模式 GoF 的《設計模式》為我們實現高性能、易擴展的J2EE應用提供理論保障和技術支持。而EJB作為J2EE的核心組件和技術,善用設計模式對系統性能影響很大。Service Locator 和Value Object 已為我們所熟悉,Floyd Marinescu的《EJB Design Patterns》中的Session Fa?ade、Message Fa?ade、EJB Command和Data Transfer Object等設計模式更是為我們提供設計典范:緩存對EJBHome的訪問;使用門面模式,不暴露Entity Bean,用Session Bean封裝Entity Bean;如果可以異步處理,則用MDB代替Session Bean;封裝業務邏輯在輕量級JavaBean中;使用值對象等簡單對象傳遞數據;不直接使用get/set方法操作Entity Bean。當然過度使用模式或者牽強套用模式也是不提倡的,總的原則就是減少網絡流量,改進事務管理。 1.5.2 使用EJB和WebLogic的特性 使用EJB和WebLogic的新特性往往能提高性能。與EJB2.0特性相關的技巧有:一個Application中使用本地接口,對于Entity Bean肯定使用本地接口,避免遠程調用的開銷;使用CMP管理關系,而不是BMP,EJB2.0中CMP的性能大大改善,性能和移植性都優于BMP;使用ejbSelect進行內部查詢;使用home方法進行外部查詢和批處理; 數據庫驅動級聯刪除等。 與WebLogic特性相關的技巧有:使用自動生成主鍵,WebLogic為Oracle和Sqlserver兩種數據庫的CMP提供了自動生成主鍵功能,節約了Entity Bean產生主鍵的時間,同時設key-cache-size不小于100;WebLogic管理事務性能更好,使用容器管理,而不是Bean管理事務;WebLogic提供了為CMP動態查詢和批量插入功能,對性能也有很大幫助。 1.5.3 緩存資源 對SLSB或者MDB來說,使用setMesssageDrivenContext()或者ejbCreate()方法緩存特定資源,在ejbRemove()方法里釋放; 對SLSB或者MDB來說,使用setSessionContext()或者ejbCreate()方法緩存特定資源,在ejbRemove()方法里釋放;對Entity Bean來說,使用setEntityContext ()方法緩存特定資源,在unSetEntityContext ()方法里釋放。 1.5.4 如何選擇和使用Entity Bean 1. 在設計EJB時,要適當考慮EJB的粒度, 細粒度的EJB在事務管理和資源管理的開銷太大,盡量創建粗粒度的 EJB , 不要太粗,粗到能滿足實際需求就可以; 2. Entity Bean不是唯一方式,如果只有一個很小的數據子集被經常改變,建議采用JDO; 3. 在操作大數據量的時候,直接采用JDBC比Entity Bean更有效; 4. 避免采用返回很大數據組的finder方法,如 FindAll() 方法,因為它的實現代價太大; 5. 考慮設置域組field groups,減少沒有必要并昂貴的屬性加載,如BLOB; 6. 對于EJB1.1或者BMP,可以設置is-modified-method-name屬性,根據isModified()的值來判斷是否調用ejbStore()等方法,減少沒有必要運算; 7. 避免連接多個表創建BMP,可以使用視圖,存儲過程或者O/R Mapping等方式。 1.5.5 其他的一些小技巧 1. 考慮使用 javax.ejb.SessionSynchronization 接口,提供在Rollback之后恢復數據的方法: afterBegin(), beforeCompletion(), afterCompletion(); 2. 使用完SFSB之后,調用remove()方法釋放實例; 3. 假如你不需要EJB服務的時候,建議使用普通Java類; 4. 避免EJB之間相互調用; 5. 使用多讀模式。 第二章 應用服務器調優 2.1 JVM調優 2.1.1 垃圾收集和堆大小 垃圾收集(GC)是指JVM釋放Java堆中不再使用的對象所占用的內存的過程,而Java堆(Heap)是指Java應用程序對象生存的空間。堆大小決定了GC的頻度和時間。堆越大,GC頻度低,速度慢。堆越小,GC頻度高,速度快。所以GC和堆大小是一組矛盾。為了獲取理想的Heap堆大小,需要使用-verbosegc參數(Sun jdk: -Xloggc:<file>)以打開詳細的GC輸出。分析GC的頻度和時間,結合應用最大負載所需內存情況,得出堆的大小。 通常情況下,我們建議使用可用內存(除操作系統和其他應用程序占用之外的內存)70-80%,為避免堆大小調整引起的開銷,設置內存堆的最小值等于最大值即:-Xms=-Xmx。而為了防止內存溢出,建議在生產環境堆大小至少為256M(Platform至少512M),實際環境中512M~1G左右性能最佳,2G以上是不可取的,在調整內存時可能需要調整核心參數進程的允許最大內存數。對于sun和hp的jvm,永久域太小(默認4M)也可能造成內存溢出,應增加參-XX:MaxPermSize=128m。建議設置臨時域-Xmn的大小為-Xmx的1/4~1/3, SurvivorRatio為8。 為了獲得更好的性能,建議在啟動文件設置WebLogic為產品模式,此時sun和hp jvm JIT引擎為-server,默認情況下打開JIT編譯模式對性能也有幫助。調整Chunk Size和Chunk Pool Size也可能對系統的吞吐量有提高。此外還需關閉顯示GC: -XX:+DisableExplicitGC。 當然在Intel平臺上使用jRockit(使用參數-jrockit)無疑大大提高WebLogic性能。 2.1.2 jRockit調優 jRockit支持四種垃圾收集器:分代復制收集器、單空間并發收集器、分代并發收集器和并行收集器。默認狀態下,JRockit使用分代并發收集器。要改變收集器,可使用-Xgc:<gc-name>,對應四個收集器分其他為gencopy, singlecom, gencon以及parallel。為得到更好的響應性能,應該使用并發垃圾回收器:-Xgc:gencon,可使用-Xms和-Xmx設置堆棧的初始大小和最大值,要設置護理域-Xns為-Xmx的10%。而如果要得到更好的性能,應該選用并行垃圾回收器:-Xgc: parallel,由于并行垃圾回收器不使用nursery,不必設置-Xns。 如果你的線程大于100或者在linux平臺下,可以嘗試使用瘦線程模式:-Xthinthread,同時關閉Native IO:-Xallocationtype:global。 jRockit 還提供了強大的圖形化監控工具Jrockit Management Console。欲詳細了解JRockit可訪問:http://edocs.bea.com/wljrockit/docs81/index.html。 2.2 Server調優 WebLogic Server的核心組件由監聽線程,套接字復用器和可執行線程的執行隊列組成。當服務器由監聽線程接收到連接請求后,將對它的連接控制權交給等待接收請求的套接字復用器。然后套接字復用器讀取離開套接字的請求,并將此請求及相關安全信息或事務處理環境一起置入適當的執行隊列中(一般為默認的執行隊列)。 當有一個請求出現在執行隊列中時,就會有一個空閑的執行線程從該隊列中取走發來的該請求,并返回應答,然后等待下一次請求。因此要提高WebLogic的性能,就必須從調整核心組件性能出發。 2.2.1 盡量使用本地I/O庫 WebLogic Server有兩套套接字復用器:Java版和本地庫。采用小型本地庫更有效,盡量激活Enable Native IO(默認),此時UNIX默認使用CPUs+1個線程,Window下為雙倍CPU。如果系統不能加載本地庫,將會拋出java.lang.UnsatisfiedLinkException,此時只能使用Java套接字復用器,可以調整socket readers 百分比,默認為33%。該參數可以在Console Server Tuning Configuration配置欄里設置。 2.2.2 調整默認執行線程數 理想的默認執行線程數是由多方面的因素決定的,比如機器CPU性能、總線體系架構、I/O、操作系統的進程調度機制、JVM的線程調度機制。WebLogic生產環境下默認的線程為25個,隨著CPU個數的增加,WebLogic可以近乎線性地提高線程數。線程數越多,花費在線程切換的時間也就越多,線程數越小,CPU可能無法得到充分利用。為獲取一個理想的線程數,需要經過反復的測試。在測試中,可以以25*CPUs為基準進行調整。當空閑線程較少,CPU利用率比較低時,可以適當增加線程數的大小(每五個遞增)。對于PC Server 和Window 2000,則最好每個CPU小于50個線程, 以CPU利用率為90%左右為佳。由于目前WebLogic執行線程沒有縮小線程數的功能,所以應將參數Threads Increase設置為0,同時不應改變優先級的大小。 2.2.3 調整連接參數 WebLogic Server用Accept Backlog參數規定服務器向操作系統請求的隊列大小,默認值為50。當系統重載負荷時,這個值可能過小,日志中報Connection Refused,導致有效連接請求遭到拒絕,此時可以提高Accept Backlog 25%直到連接拒絕錯誤消失。對于Portal類型的應用,默認值往往是不夠的。Login Timeout和SSL Login Timeout參數表示普通連接和SSL連接的超時時間,如果客戶連接被服務器中斷或者SSL容量大,可以嘗試增加該值。這些參數可以在Console Server Tuning Configration配置欄里找到。 2.2.4 創建新的執行隊列 創建新的執行隊列有助于解決核心業務優先、避免交叉阻塞、死鎖和長時間處理的業務等問題。通常會將自己的執行隊列和默認的執行隊列設置不同的優先級,這里優先級不應設為9或者10。 定義一個新的執行隊列很容易,利用View Excute Queue選項中的Configure a new Excute Queue鏈接即可定制新的執行隊列。創建新的執行隊列后,用戶需要為應用程序的J2EE組件配置分配策略,以便它可以找到新的隊列。舉個例子:要將servlet或jsp捆綁到一個特定的執行隊列,必須替換web.xml文件項,將wl-dispatch-policy初始化參數設置為自己的執行隊列名。
<servlet> <servlet-name>servletname</servlet-name> <jsp-file>/directoryname/deployment.jsp</jsp-file> <init-param> <param-name>wl-dispatch-policy</param-name> <param-value>NewExecuteQueueName</param-value> </init-param> </servlet> 我們可以為一個jsp或者servlet乃至一個WEB應用設置自己的執行隊列。同時也可以為EJB設置自己的執行隊列。對于執行時間比較長的MDB,建議使用自己的執行隊列。 2.3 JDBC調優 2.3.1 調整連接池配置 JDBC Connection Pool的調優受制于WebLogic Server線程數的設置和數據庫進程數,游標的大小。通常我們在一個線程中使用一個連接,所以連接數并不是越多越好,為避免兩邊的資源消耗,建議設置連接池的最大值等于或者略小于線程數。同時為了減少新建連接的開銷,將最小值和最大值設為一致。 增加Statement Cache Size對于大量使用PreparedStatement對象的應用程序很有幫助,WebLogic能夠為每一個連接緩存這些對象,此值默認為10。在保證數據庫游標大小足夠的前提下,可以根據需要提高Statement Cache Size。比如當你設置連接數為25,Cache Size為10時,數據庫可能需要打開25*10=250個游標。不幸的是,當遇到與PreparedStatement Cache有關的應用程序錯誤時,你需要將Cache Size設置為0。 盡管JDBC Connection Pool提供了很多高級參數,在開發模式下比較有用,但大部分在生產環境下不需調整。這里建議最好不要設置測試表, 同時Test Reserved Connections和Test Released Connections也無需勾上。 當然如果你的數據庫不穩定,時斷時續,你就可能需要上述的參數打開。 最后提一下驅動程序類型的選擇,以Oracle為例,Oracle提供thin驅動和oci驅動,從性能上來講,oci驅動強于thin驅動,特別是大數據量的操作。但在簡單的數據庫操作中,性能相差不大,隨著thin驅動的不斷改進,這一弱勢將得到彌補。而thin驅動的移植性明顯強于oci驅動。所以在通常情況下建議使用thin驅動。而最新驅動器由于WebLogic server/bin目錄下的類包可能不是最新的,請以Oracle網站為準:http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/htdocs/jdbc9201.html。 2.4 WEB調優 2.4.1 調整WEB應用描述符 WEB應用除代碼之外的調優比較簡單,僅僅是對一些WEB應用描述符的調整。首先關閉Session Monitoring Enabled,僅僅在Cluster環境下設置Session復制(優先使用內存復制),在保證應用正常運行的情況下,設置較短的Session超時時間。 同時生產環境下無需檢查Jsp和servlet:JSPPage Check Secs和Servlet Reload Check Secs均設為-1,關閉JSPKeep Generated 和JSPVerbose對性能也有幫助。此外,還可以對jsp進行預編譯,有兩種方法:激活precompile選項;使用weblogic.appc事先編譯,建議采用后者。 2.5 JMS調優 1. 增加-Dweblogic.JMSThreadPoolSize=n(至少為5),以提高處理JMS的線程數,在jRockit上增加-XXenablefatspin以減少加鎖沖突; 2. 采用文件存儲策略,將同步寫策略設置為Direct-Write,同時在windows平臺上啟用磁盤寫入緩存; 3. 使用分布式目的地時,激活連接工廠Load Balancing Enabled ,Server Affinity Enabled; 4. 為減少服務器不必要的JMS請求路由,如果多個目的地之間存在事務,則部署在同一JMS服務器上,盡量將連接工廠部署到JMS服務器所在的WebLogic實例上,集群環境下,則最好將連接工廠部署到集群中的所有服務器上,而集群中每個JMS服務器和目的地成員盡量使用類似的設置; 5. 啟用消息分頁存儲功能,以釋放內存,可以為JMS服務器和目的地設置, 激活Messages Paging Enabled和Bytes Paging Enabled,同時使用限額防止服務器耗盡接收消息的所有可用內存空間; 6. 在運行WebLogic Server進程之外的生產者務必使用流控制, 并增大Send Timeout; 7. 將JMS Server Expiration Scan Interval設很大的值,能禁止主動掃描過期消息; 8. 使用FIFO或者LIFO方式處理目的地消息; 9. MDB的max-beans-in-free-pool不應大于最大MDB線程數(默認線程數/2+1)。 6 EJB調優 2.6.1 調整pool和cache initial-beans-in-free-pool定義SLSB啟動時實例的個數,默認為0,可以調大到正常并發數的大小,以減少初始響應時間。max-beans-in-free-pool為最大個數,默認1000對SLSB來說,在頻繁創建和刪除實例的情況下很有幫助,一般不用調整,至少設為默認線程數,過大容易造成內存溢出。而對Entity Bean來說,由于是匿名的,所以當頻繁使用finder、home和create方法時可以調大。 對SFSB來說,盡量將max-beans-in-cache參數設置得足夠的大,以滿足Bean實例對最大并發用戶數的要求,可以避免有狀態會話Bean過多的鈍化行為。而idle-timeout-seconds盡量設置小,如果SFSB不用于存儲Web應用會話狀態可以設置為0。 對于Entity Bean來說, max-beans-in-cache同樣可以首先采用默認值1000,監控實例緩存和鈍化的情況,再做適當調整。 并行策略concurrency-strategy定義了實體Bean如何管理鎖,有四種策略: Exclusive、Databse、ReadOnly、Optimistic。效率依次提高,可靠性依次降低,盡量避免使用互斥策略,如果Bean無需更新操作,使用只讀策略,更甚的是,如果Bean的內容不會改變,可設置read-timeout-seconds為0,樂觀并行策略時采用事務間緩存策略,在entity-cache描述符中將cache-between-transactions元素設為true。 對EJB組件來說,有四種事務隔離水平: 第三章 數據庫調優 3.1.1 Oracle性能優化 Oracle9i的性能優化除了調整kernal之外就是主要對Oracle啟動文件的調整,即調整SGA的參數。注意,不同操作系統不同位數的機器最優的參數不是一樣的,這里主要有windows和unix之分,32位和64位之分。 首先需要調大進程數和游標數,一般默認的值對實際應用來說都比較小,比如說,進程數可以調到300,游標數可以調到500。 其次,看一個經驗公式: OS 使用內存+ SGA + session*(sort_area_size + hash_area_size +2M)<0.7RAM,通常認為此時的SGA比較合理。這里sort_area_size為64k, hash_area_size為128k(當排序多的時候需要增大sort_area_size,按調整后的值計算),session表示最大并發進程數,假設100個。假如1G內存的機器,OS占用200M,PGA占用200M左右,那么SGA可以設為400-500M,如果2G內存可以1G給 SGA,8G可以5G給SGA。不過對于32位數據庫來說,通常最多只能使用1.7G內存。 然后,SGA內參數設置的基本原則是: data buffer 通常可以盡可能的大,shared_pool_size 要適度,log_buffer 通常大到幾百K到1M就差不多。具體的:data buffer 1G內存可以設置500M,2G設為1.2G,8G可設為5G 。shared_pool_size不易過大,通常應該控制在200M--300M,如果使用了大量的存儲過程,可以根據SGA的值增大到500M,如果增大后命中率得不到提高,則增加是無益的。具體的:1G內存可以設置100M,2G設為150M,8G可設為300M。如不使用Java, java_pool_size 10-20M即可。large_pool_size如果不設置MTS,在20M -30M 即可,假如設置 MTS,可以考慮為 session * (sort_area_size + 2M)。 最后,關于內存的設置可根據statspack信息和v$system_event,v$sysstat,v$sesstat,v$latch 等view信息來考慮微調。 3.1.2 Oracle的其他調整 為了Oracle高效率的運行,除了上面提到的內存因素之外,還有就是需要良好的數據庫設計:表、視圖、索引和日志的合理規劃和建立。I/O的性能也是重要因素,應盡量減少頁交換和頁分配。此外,就是改善檢查點的效率。 第四章 操作系統調優 4.1 操作系統調整 操作系統影響應用程序運行性能的因素主要有:硬件的配置(CPU、內存、硬盤等),核心參數,TCP/IP參數以及補丁的情況等。這里對操作系統的優化,除了更新最新的補丁程序以保證應用程序正常運行之外,就是調整TCP/IP參數,文件描述符,對于個別操作系統還有其他特別的參數調整。下面將依次介紹不同操作系統的情況,更多的信息請參考各操作系統的文檔。 4.1.1 HP-UX 對于HP-UX,你首先需要安裝Java Patch: http://www.hp.com/products1/unix/java/patches/index.html,然后需要確認下面文檔中的核心參數是否滿足(可以使用sam命令修改核心參數):http://e-docs.bea.com/platform/suppconfigs/configs81/hpux11_risc/81sp3.html#80105。 調整TCP參數: ndd -set /dev/tcp tcp_conn_req_max 1024, 將偵聽隊列的最大允許長度調整到1024。 有時操作系統限制進程使用的最大內存數小于你要配置的內存大小,則需要調整該值。 讀者可以從http://docs.hp.com/hpux/onlinedocs/TKP-90203/TKP-90203.html了解更多的HP-UX調整建議。 4.1.2 Solaris 調整TCP的參數,等待時間間隔tcp-time-wait-interval建議設置為60000ms: /usr/sbin/ndd ?set /dev/tcp tcp_time_wait_interval 60000; 其他參數調整如下: tcp_xmit_hiwat/tcp_recv_hiwat 131072 tcp_conn_req_max_q/tcp_conn_req_max_q0 16384 調整一個進程打開的文件描述符的數量:軟限制和硬限制以及散列表的大小,修改/etc/system文件: set tcp:tcp_conn_hash_size=32768 set rlim_fd_cur=8192 set rlim_fd_max=8192 更多的調整信息請查閱: http://docs.sun.com/db/doc/806-7009(Solaris9)。 4.1.3 AIX AIX用no命令調整TCP參數,等待時間間隔tcp_timewait: no -o tcp_timewait=4,將tcp.timewait參數設置為4個15秒間隔,即1分鐘。運行no -a命令將顯示網絡當前的所有屬性值。由于UDP_SENDSPACE默認的緩存大小是8k,為減少I/O異常,需調整為32k: no -o udp_sendspace=32768。此外, 當WebLogic HTTP請求忙時,可以調整偵聽隊列的最大長somaxconn到8192(默認值是1024)。 更多信息:http://publib16.boulder.ibmo.com/pseries/en-us/aixbman/prftungd/prftungd.htm。 4.1.4 Linux 調整Linux系統使用sysctl命令修改TCP參數等待時間間隔:sysctl -w ip_ct_tcp_timeout_time_wait=60;調整打開文件的最大數:在/etc/sysctl.conf文件中,添加: Fs.file-max=65535,然后運行sysctl -p;調整打開文件描述符最大數為8192:在/etc/security/limits.conf文件,添加:WebLogic hard nofile 8192(僅針對WebLogic用戶),然后在WebLogic啟動文件里運行ulimit-n 8192激活設置。 更多信息請查閱:http://ipsysctl-tutorial.frozentux.net/ipsysctl-tutorial.html。 4.1.5 Windows Windows系統的調整通過修改注冊表HKEY-LOCAL-MACHINESYSTEMCurrent ControlSetServices文件夾來完成。可以調整TcpipParameters子文件夾中的等待時間間隔時間 TcpTimedWaitDelay參數的值。偵聽隊列最大長度的默認值為15,為修改它,可在InetinfoParameters子目錄中創建 DWORD條目ListenBackLog。 此外,Windows2000的Service Pack(要求sp3以上)也會影響系統穩定性: http://e-docs.bea.com/platform/suppconfigs/configs81/win2ksvr_as_data_pentium/81sp3.html。 第五章 性能監控和性能分析 5.1 性能瓶頸 最后,介紹一下實際分析J2EE應用性能的常用命令和工具。對于實現一個高性能的J2EE應用來說,掌握了J2EE調優的理論經驗還是不夠的。掌握性能監控,發現瓶頸和問題診斷才是保證J2EE系統持續高效運行的關鍵。 瓶頸指的是限值所有吞吐操作以及嚴重影響反應時間的系統內資源。在分布式系統內尋找并糾正瓶頸是非常困難的,需要有經驗的團隊來解決。瓶頸會發生在Web服務器上,程序代碼中,應用服務器上,數據庫,操作系統或者網絡,硬件上。經驗表明,瓶頸很容易發生在如下地方:數據庫連接與隊列中;應用服務器的程序代碼中;應用服務器和Web服務器硬件上;網絡和TCP配置中。實際中可以著力對這些環節進行監控。 5.2 操作系統監控 操作系統層面的性能監控主要是對內存、CPU、I/O和交換區的使用情況進行監控分析。windows平臺可以通過任務管理器和perfmon工具查看。如果是unix系統可以使用stat系列命令(vmstat, mpstat, iostat)監控內存、CPU和I/O的即時變化,使用swap命令查看交換區的使用情況。如果操作系統安裝了top、topas、glance等使用工具,則使用top、topas、glance將能更為方便地看到WebLogic進程對操作系統的內存,CPU和I/O資源使用的即時變化情況。 而網絡方面的性能可以通過ping和netstat等命令來監控,主要幾個關鍵的網絡統計值,如數據包再發送、重復數據包和數據包偵聽丟失。 說明:本文提到的unix命令并非適用所有操作系統,僅供參考。 5.3 數據庫監控 數據庫層面的監控這里為oracle9i為例來說明,可以采用oracle自帶的工具Oracle? Interprise Manager Console來監控session和sql的執行情況。還有其他專業的數據庫監控工具可以使用,比如QUEST的spotlight(http://www.quest.com/spotlight-portal/)可以非常形象和直觀地對Oracle數據庫的CPU、內存、I/O、Data Buffer Size、Shared Pool Size、Redo Buffer等參數進行即時監控,并自動對不正常的參數以紅色顯示。 5.4 WebLogic監控 5.4.1 JVM監控 采用java參數-verbose:gc 來分析JVM的GC非常繁瑣,而且不直觀。使用-Xloggc:gc.log 參數將GC日志寫入文件,采用GC 工具HPjtune (http://www.javaperformancetuning.com/tools/hpjtune/index.shtml)進行分析,可以輕松看出當前jvm參數配置是否合理。 嚴格意義上來說HPjtune是一個分析工具,不是監控工具。這里不得不提及jRocket,Intel平臺上最快的JVM, 在WebLogic啟動命令中增加-Xmanagement參數,就可以執行beajrockit81sp3_142_04 in下console命令監控WebLogic的內存使用和CPU負載情況。設置Tools/Preferences菜單中的Mode of operation屬性為developer, jRocket將提供Method Profiler工具,她能夠將所有在JRockit Java虛擬機上執行的成員方法的調用次數、執行的總時間和每次調用的執行時間都統計出來,進行代碼級調優,這是jRockit的又一大優勢。 5.4.2 Console監控 WebLogic Console除了管理配置功能之外,提供了豐富的監控功能。通過WebLogic Console,首先我們可以查看服務器的運行情況。 5.4.2.1 Server監控 通過使用服務器的Performance Monitoring選項卡,可以查看到請求吞吐量,執行隊列積壓情況以及JVM棧利用情況。而通過點擊Performance Genaral選項卡中” Monitor all Active Queues...”可以查看所以執行線程的當前統計數據。此外Monitoring選項卡還可以監控JTA和JMS等Service的情況。 5.4.2.2 JDBC監控 在連接池Monitoring選項卡中,WebLogic Console為每一個數據庫連接池提供了實時統計信息。其中有三個重要參數可以反應WebLogic Server的健康狀況:Connections High、Wait Second High和Waiters High。Connection High表示從服務器啟動開始后到達池的最大連接數量,如果大于池的最大數量,則需要調整Maxium Capacity。Waiters High表示在沒有可用連接的情況下,應用程序等待連接的最大個數。我們可以根據Waiters High的大小調整連接池容量。更多的參數可以通過Customize this view鏈接添加,參數含義參考:http://e-docs.bea.com/wls/docs81/ConsoleHelp/domain_jdbcconnectionpool_monitor.html#1104829。 5.4.2.3 WEB監控 Web Application Monitoring選項卡可以監控WEB應用的Session個數,以及Servlet的響應情況,激活Session Monitoring Enabled可以獲取所有session的統計情況。更多信息請參考: http://e-docs.bea.com/wls/docs81/ConsoleHelp/web_applications.html#1106723。 5.4.2.4 JMS監控 Welogic Console JMS監控功能比較多,不僅在Server JMS Monitoring選項卡可以監控Active JMS Connections, Pooled JMS Connections和Active JMS Servers的連接和使用情況。還可以監控JMS Session Pool、Active JMS Destinations和Durable Subscribers的消費和生產情況。比如,我們可以監控到JMS Queue的接收和消費消息的數量和字節數。有關JMS監控的詳細情況可參見:http://edocs.bea.com/wls/docs81/ConsoleHelp/jms_monitor.html。 5.4.2.5 EJB監控 EJB監控包括對SLSB,SFSB,Entity Bean,MDB四種EJB的監控。本人認為EJB監控提供了非常豐富的運行時統計信息(http://e-docs.bea.com/wls/docs81/ConsoleHelp/ejb.html#1105036),非常有利于我們對EJB進行性能調優。 SLSB選項卡為用戶提供實例池的運行時統計信息。Pool Miss Ratio 表示實例池的Miss率,Pool Waiter Total Count 表示線程等待bean 實例的累計時間,Pool Timeout Total Count表示超時的線程數。當Pool Miss Ratio較大時,可以增加max-beans-free-pool。 SFSB可以關注Cache Miss Ratio和Activation Count。Cache Miss Ratio過大時,調大max-bean-in-cache未必有幫助,需要嘗試不用的max-bean-in-cache以獲得最低的Cache Miss Ratio。激活將嚴重減慢應用程序的速度,如果某一個bean的Activation Count的值過高,那么需要考慮增加max-bean-in-cache的大小。 Entity Bean結合了SLSB的free pool和SFSB的cache。可以結合上面的策略進行監控。 而MDB僅比SLSB多一個參數JMSConnection Alive,報告EJB是否成功連接到JMS目的地。 更多Console監控信息可參見http://edocs.bea.com/wls/docs81/ConsoleHelp/index.html。 5.4.3 實用工具分析 WebLogic除了提供Console進行應用監控之外,用戶還可以編寫JMX程序或者通過SNMP協議進行監控。而QUEST Spotlight for WebLogic Server提供了類似WebLogic Console類似的監控功能,并對異常情況顯紅。 這里不得不提到實戰中經常用來分析性能瓶頸的工具THREAD DUMP,統一的命令是使用 weblogic.Admin 命令 THREAD_DUMP。而在 windows上還可以使用<Ctrl>+<Break> 來創建診斷問題所需的線程轉儲Thread Dump,而在unix上使用kill -3 <wlspid>命令。我們從中可以看到WebLogic后臺線程的運行情況,通常需要每隔10秒左右持續執行幾次以助診斷問題。更多信息可以參考BEA實戰集錦。 5.5 應用程序分析 應用程序分析除了憑借程序員豐富的經驗和敏銳的洞察力去人工檢查代碼之外,使用廠家的工具也是節省時間的不錯選擇。目前市場上有Borland Optimizeit Enterprise Suite和QUEST Jprobe兩個產品可以用來分析性能瓶頸,垃圾收集,內存泄漏,線程死鎖和代碼復蓋等。Hpjmeter是一個免費的工具,也具有以上類似的性能分析功能。 而Borland Optimizeit Server Trace,HP OpenView Transaction Analyzer和Mercury LoadRunner J2EE breakdown都可以用來分解J2EE應用從客戶端訪問到最終數據庫操作每一層次花費的時間,甚至精確到每一個方法的執行時間。Server Trace還具有檢查內存泄漏,連接泄漏和錯誤警告等功能,一般在測試環境中使用。而HP OTVA的優勢在于運行時監控,LoadRunner優勢在于壓力測試。 總結 J2EE調優是一門實踐和經驗科學,是一個復雜而往復的過程。其原則是:合理。合理,看似簡單,細細品味,意味深長。本文所述的調優策略并不是一成不變的,只是為了給大家一個參考建議,讓大家少走彎路,關鍵是根據實際環境調優。歡迎有興趣的朋友在論壇上積極討論和批評指正。 參考文獻 [1]BEA WebLogic Server edocs: http://e-docs.bea.com/wls/docs81/perform/index.html [2]Gregory Nyberg & Robert Patrick :Mastering BEA WebLogic Server [3]Jack Shirasi:Java Performance Tuning |