急性者的效能優化

sqiutz發表於2010-03-05

引言

如果您是這樣一個人:啟動並執行 WebSphere® Application Server 後就忙著處理其他事情,而沒有時間研究與效能優化相關的文件(請參閱參考資料)。那麼,您來對地方了——本文旨在幫助您確定可能帶給您 80% 效能改善的 20% 的效能更新。本文重點介紹 WebSphere Application Server base 產品——下一篇文章將介紹 WebSphere Application Server Network Deployment。

應用程式基礎設施中的優化帶來的效能改善不能與修復編得很差的應用程式所獲得的顯著效能改善同日而語。 一些應用程式在其資料庫查詢中沒有 where 子句;一些應用程式呼叫了 wait() forever;一些應用程式構建了死鎖生成器;一些應用程式擁有龐大的 HTTPSession;一些應用程式執行了 select * 並試圖將所有資料快取到中間層中(有千兆的資料也那麼幹!)。本文將向您介紹一些很好的優化技巧,但只有應用程式本身可以帶來真正顯著的結果。是的,這意味著應用程式開發人員和伺服器管理員必須定期相互溝通!

本文涵蓋以下內容:

  • 效能測試環境
    • 找出薄弱環節
    • 測試工具
    • 模擬本文所使用的環境
  • 環境衛生
    • 獲得效能基準數
    • 優化記錄/日誌
    • CPU 使用率
    • 記憶體使用率
    • 其他環境因素
  • 實現 Jython 經驗優化
    • JVM 冗餘垃圾回收 (GC)
    • JVM 設定
    • 連線池設定
    • 啟用 Servlet 快取
    • Web 容器執行緒池
    • 高階優化思想:SpecJ 全面披露

效能測試環境

找出薄弱環節

您需要一個儘可能反映生產環境的測試環境。如果您使用的是幾千兆的資料倉儲,則測試系統可能需要小一些。但是效能和生產環境之間的任何差異都可能引起高開銷的推斷錯誤。大型企業沒有任何理由宣稱它們無法提供同等規模的效能測試伺服器。小測試環境可能無法暴露某些方面(例如鎖操作、日誌記錄、HTTPSessions、垃圾回收、連線池、CPU、記憶體、資料庫、網路或應用程式)現有的問題。

測試工具——找準方向

建立實際測試工具是有效優化的關鍵步驟。如果您在測試伺服器上施加的工作負載無法反映出站點中實際發生的情況,則您的優化將無法解決問題!有很多開放原始碼的軟體測試產品和商業軟體測試產品(請參閱下面的參考資料)。您可以通過檢視現有的伺服器日誌來了解實際測試場景。當引入新的應用程式時,這些歷史記錄對您是沒有幫助的,所以必須有最好的設想。

測試工具——打破極限

優化旨在發現問題並修復它們。如果測試沒有使伺服器負載達到臨界點,則有些問題將檢測不到,所以要確保伺服器的負載超過您預期的最高峰流量。這樣做將使您獲得測試工具定義中的誤差幅度。優化最差情況下的負載,這樣您在投入生產時就已經擁有更高負載下的效能值。於是,您將知道您的安全幅度有多大以及什麼時候需要為非常流行的應用程式進行擴容。應用程式的建立者不應該開發測試工具,因為他們知道假定的邊界條件,會有意避免建立超出這些邊界的測試用例。生產工作負載可沒有這麼友善!

一些測試提供的使用者數是適度增長的,從而生成相當線性化的效能曲線圖。但是實際的使用者到達率往往是叢集化且不規則的,所以在投入生產環境之前需要對這些條件進行測試。

如果您的測試計算機上有其他使用者,則他們必須知道您的測試計劃——否則他們可能認為他們遭遇某種嚴重的問題,例如拒絕服務攻擊。

本文所使用的測試環境

本文中的結果來自 IBM 培訓計劃,它使用 WebSphere Trade6 內部基準來訓練學員在高壓競爭環境下進行效能優化。該測試環境使用 Red Hat Enterprise Linux® V3、WebSphere Application Server V6.0.1、DB2® V8.2 和 Apache JMeter。這些產品將安裝在 VMWare V5.0 下。兩臺 IBM ThinkPad® 行動式計算機使用乙太網交叉電纜相連線。應用伺服器執行在一臺計算機上,而測試工具和資料庫執行在另一臺計算機上。

為了簡化效能優化環境和降低出現錯誤的可能性,您應該使用 shell 指令碼來啟動不同的元件。下面的清單 1 顯示了一個名為 go_trade6 的示例指令碼。該指令碼從端到端啟動基準,包括 WebSphere Application Server、DB2 伺服器、JMeter 程式和用於監控 top 和 vmstat 的 xterm。


清單 1. 啟動測試環境所有元件的指令碼
xterm -exec top &
xterm -exec vmstat 3 &
su - -c db2start db2inst1
#su - -c oninit informix 
startWAS
mozilla &
# start test harness
cd /root/trade6/jakarta-jmeter-2.0.2/bin
/opt/IBMJava2-141/bin/java ApacheJMeter.jar -t tradebuysell.jmx &
# read wastecpu.c 
wastecpu & 
wastecpu & 
export PATH=$PATH:/opt/IBM/WebSphere/AppServer/bin

環境衛生

獲得效能基準數

在進行任何更改之前,獲得效能基準數是很重要的,因為只有與基準進行比較才能確切說明系統變快還是變慢。在執行基準測試或其他效能測試時,必須謹慎地控制系統上的其他工作。例如,如果您的資料庫用於事務處理和繁重的決策支援 (DSS) 查詢,則可以確定非預定的 DSS 查詢將犧牲您的事務效能。如果您在不知道 DSS 會有干擾時試圖優化此環境,則將錯誤地得出結論:系統的效能“已優化,但不可預測”。實驗示例的效能基準(使用 JMeter)如下面的圖 1 所示。可以通過 Run 選單下拉選單啟動一項測試或清除先前的所有資料以進行下一次測試。


圖 1. 使用 JMeter 的效能基準:37 毫秒響應時間
圖 1. 使用 JMeter 的效能基準:37 毫秒響應時間

基準效能表明平均響應時間為 37.6 毫秒(請看 JMeter 幀的右下角)。您可以對不同的度量進行優化,例如吞吐量、中值響應時間、響應時間偏差或其他某種優化目標。首先確定優化目標,然後弄清楚如何優化系統才能實現此目標。

如果工作負載很大而且系統優化不好,則需要花很長時間才能獲得基準效能值。您可以使用小型測試工具來縮短一些早期測試的時間,還可以獲得系統輸出的詳細資訊。確保數千次點選的次秒級響應不會只是應用程式錯誤頁面的詳盡測試。JMeter 提供測試活動日誌。您可以定義一個檔案(例如 /tmp/jmeter.out)來包含 HTTP 請求響應活動的返回程式碼。

在下面的下載部分,您將發現 JMeter 的兩個不同的測試檔案。一個是完全測試,另一個標為“Small”。Small 測試也啟用了輸出收集功能,以便您可以看到請求-響應的結果。在實驗測試中,計算機是完全滿負荷的——圖 1 右下角的藍色實框表示 100% 的 CPU 使用率。圖形指示器和執行 top 命令的 xtermBoth 都指示 CPU 使用率的問題。查明什麼佔用了 CPU 時間需要費些周折。

沒有足夠的 CPU 和記憶體,伺服器(WebSphere Application Server 或其他任何伺服器)將不能很好地執行。如果您在沒有空閒 CPU 時或者在作業系統需要交換虛擬記憶體的地方試圖優化伺服器,則無法使其變得很快。下一部分將描述如何檢測 CPU 或記憶體受限的系統以及如何修復它們。

優化日誌

一些優化更改將使系統執行得更快,而一些將使之執行得更慢。記錄您做了什麼更改以及為什麼這樣更改將使優化過程更簡單也更順利。下面是一個非常簡單的配置日誌——格式不是很重要,只要您每次記錄一些關鍵資料點即可。一個好的過程是將更改作為註釋放在配置檔案中。


示例優化日誌條目
DateTime:_____________   Name:_____________________
Parameter Changed____________________________________
Why?________________________________________________
How?________________________________________________
New Performance____________________________milliseconds
Keep____________ Remove_____________ How?___________

CPU 使用率

在進行任何優化前,系統必須有可用的 CPU 週期。任何在伺服器上執行的不是很有用的程式都應該停止或禁用——包括在伺服器上執行的漂亮螢幕保護程式!檢測 CPU 受限的系統的最佳方式是執行 top 命令——它是 Linux/Unix 效能工具的“瑞士軍刀”:


圖 2. top 命令
圖 2. top 命令

在右上角,“idle”處於 0.0%,所以我們知道計算機已竭盡全力在執行。“COMMAND”欄中的程式列表顯示佔用最多 CPU 週期的兩個程式稱為 wastecpu。如果您閱讀清單 2 中的註釋,您會發現可以將wastecpu 殺死。


清單 2. wastecpu 原始碼
/* if you read this comment before you killed "wastecpu" you did good!
Wastecpu is meant to simulate a "production application" and the purpose 
is to train people not to kill ANYTHING without knowing what it does.

*/
#include <stdio.h>
main()
{
int i;
for (i=0;i<1;i=0)
{
/* wow, what a useful program */
i=0;
}
} /* end of main */

在執行 top 命令時,要殺死這些模擬的 CPU 貪吃戶,請按 K 鍵,top 將詢問您要殺死什麼程式。輸入程式 id (PID)(在本例中為 2441),然後 top 將停止此程式。再次執行此操作來殺死第二個副本 PID 2442。要避免以後還要處理 wastecpu 程式,請在 go_trade6 指令碼中將其註釋掉。

在一些生產環境中,發生問題時除了“拋棄硬體”外別無他法。有許多選擇可以進行縱向和橫向擴充套件。圖 3 闡釋了用於在實驗環境中新增另一個處理器的配置。交叉電纜在學員小組(高度競爭的學員的一個重要特徵)之間提供“氣隙 (Air Gap)”安全性。


圖 3. 測試實驗中兩臺 ThinkPad 的 Trade 6 配置
圖 3. 測試實驗中兩臺 ThinkPad 的 Trade 6 配置

記憶體使用率

在有足夠記憶體時,WebSphere Application Server 執行得最好。您可以通過 top 命令檢視記憶體使用率。“交換”域應該為零——如果不是,則作業系統正在通過磁碟空間模擬有更多的 RAM 的情況,這當然是一個很慢的過程。使用 vmstat 命令可以更詳細地檢查記憶體情況。下面的圖 4 中的 vmstat 輸出表明系統不堪重負。“si”和“so”欄(swap-in 和 swap-out)與零相去甚遠。此計算機需要更多的記憶體或者減少正在執行的程式。有關“si”和“so”的更多資訊,請使用 man vmstat 獲得。


圖 4. 記憶體不足的系統——“si”和“so”應該為零
圖 4. 記憶體不足的系統——“si”和“so”應該為零

執行伺服器或者使用 VMWare 教授實驗的最好做法就是“升級”記憶體時不用開啟計算機。只需關閉客戶作業系統 (guest operating system),將設定更改為使用全部記憶體,然後啟動虛擬機器,如下面的圖 5 所示。如果虛擬機器需要比執行主機作業系統更多的記憶體,則程式執行仍然會很慢。如果有人能夠發明一個系統,它可以模擬的記憶體比硬體中現有的記憶體更多,則這些程式將會執行得非常好!


圖 5. VMWare 工作站中的記憶體升級
圖 5. VMWare 工作站中的記憶體升級

其他環境因素

環境中有許多其他因素會影響效能,包括網路使用率、伺服器上的其他程式、拒絕服務攻擊以及電源線被絆斷。請確保您的作業系統針對工作負載進行了優化。您需要修改 Linux 上的 sysctl、Solaris® 上的 /etc/system,以及AIX® 上的一組引數(請參閱下面的參考資料)。

資料庫管理員 (DBA) 能夠對效能優化做出很大的貢獻。除了調整資料庫引數外,DBA 可以識別出編得不好的查詢和死鎖生成器,這種反饋對提高效能是很重要的。DBA 還有一些靈活的工具,例如 DB2 Health Center,它可以識別問題並生成指令碼來修復這些問題。DB2 Performance Adviser 和 Index Adviser 也使 DB2 資料庫優化變得更容易。要開啟 DB2 Health Center,您可以從命令列輸入db2hc,或者單擊 DB2 Command Center 中的 Health Center 圖示。下面的圖 6 顯示了 DB2 Health Center 生成的一個警報,圖 7 顯示了它為更大的鎖列表提供的一個建議:


圖 6. 正在執行的 Health Center:生成一個警報
圖 6. 正在執行的 Health Center:生成一個警報

圖 7. 正在執行的 Health Center:推薦解決方案
圖 7. 正在執行的 Health Center:推薦解決方案

應該捕獲圖 7 中建議的命令並將其放在一個指令碼檔案中。當凌晨兩點試圖恢復系統時是沒有地方獲得 GUI 的!所有的一切都需要放在指令碼中。

實現 Jython 經驗優化方法

現在我們開始優化 WebSphere Application Server。Wikipedia.org 將經驗方法 定義為“進行近似計算或回撥某個值,或者進行某種決策的易學易用的過程”。這是進行快速而質量不高的優化的“入場券”。為什麼使用 Jython 呢?硬核管理員使用命令列而非 GUI 來執行重複的定義良好的任務。引用我們一位開發專家的話,“生命太短,不足以用 tcl 程式設計。”

JVM 冗餘垃圾回收 (GC)

JVM 在堆中執行其工作的核心部分。物件不再使用時就是垃圾(技術術語是它們不再被引用)。對於任何家居,您可以通過看其垃圾來了解居住的是什麼樣的人!Verbose GC 有助於您確定您的記憶體堆是否太多或太大。


清單 3. 優化冗餘 GC
#(c)copyright 2005
# sample code - not supported

# get help with this command in interactive mode: AdminConfig.help()

server1=AdminConfig.getid('/Node:tux1Node01/Server:server1/')
print server1
jvm = AdminConfig.list('JavaVirtualMachine', server1)
print ">>>>>  variable jvm is"
print jvm
print ">>>>>  AdminConfig.show(jvm)"
print AdminConfig.show(jvm)
print ">>>>>  change jvm settings"
AdminConfig.modify(jvm, [['verboseModeGarbageCollection','true' ]] )
AdminConfig.save()  
print ">>>>>  after save:"
print AdminConfig.show(jvm)

# on my system the output of verbose gc is in the file:
#  /opt/IBM/WebSphere/AppServer/profiles/default/logs/server1/native_stderr1.log
# your milage may vary if you change the log locations
#
# note - when using jython you must use the string 'true', see below. 
#
#wsadmin>AdminConfig.modify(jvm, [['verboseModeGarbageCollection', 0 ]] )
#WASX7435W: Value 0 is converted to a boolean value of false.
#''
#wsadmin>AdminConfig.modify(jvm, [['verboseModeGarbageCollection', 1 ]] )
#WASX7435W: Value 1 is converted to a boolean value of false.
#''

要使用以上指令碼,請執行下列操作:

  • 將其放在一個檔案中並起一個有用的名字,例如 verboseGC_on.jython
  • 確保 wsadmin.sh(或者 wsadmin,對於 Windows®)在您的命令路徑中。
  • 在命令提示符下執行 wsadmin.sh -lang jython -f verboseGC_on.jython

JVM 設定

現在您已經配置了冗餘 GC,您可以開始優化堆的大小。理想情況下 GC 週期應該:

  • 發生間隔大於 10 秒
  • 在 1 至 2 秒內完成

下面的指令碼將更改堆的大小。其目標是使堆足夠大,大到 GC 間隔大於 10 秒;而又足夠小,小到持續時間僅為 1 到 2 秒。對於每個新的 JVM 版本,GC 演算法將會得到改進,所以此優化應該會隨著時間的流逝越來越容易。


清單 4. native_stderr1.log,其 GC 間隔 6893 毫秒,持續時間 456 毫秒
<AF[14]; Allocation Failure. need 528 bytes, 6893 ms since last AF>
<AF[14]; managing allocation failure, action=1 (0/183731208) (1668600/1668600)>
  <GC(14); freeing class sun.reflect.GeneratedMethodAccessor18(0x102391b8)>
  <GC(14); freeing class sun.reflect.GeneratedFieldAccessor1(0x104e9f48)>
..... lines deleted......
  <GC(14); freeing class sun.reflect.GeneratedFieldAccessor19(0x10129030)>
  <GC(14); unloaded and freed 20 classes>
  <GC(14); GC cycle started Tue Dec 27 16;18;13 2005
  <GC(14); freed 77240288 bytes, 42% free (78908888/185399808), in 436 ms>
  <GC(14); mark; 396 ms, sweep; 40 ms, compact; 0 ms>
  <GC(14); refs; soft 52 (age > 6), weak 89, final 88, phantom 0>
<AF[14]; completed in 456 ms>


清單 5. 將 JVM 堆增加到 512 MB - 1 GB
#(c)copyright 2005
# sample code - not supported

#AdminConfig.help()
server1=AdminConfig.getid('/Node:tux1Node01/Server:server1/')
print server1
jvm = AdminConfig.list('JavaVirtualMachine', server1)
print ">>>>>  variable jvm is"
print jvm
print ">>>>>  AdminConfig.show(jvm)"
print AdminConfig.show(jvm)
print ">>>>>  change jvm settings"
AdminConfig.modify(jvm, [['initialHeapSize', 512 ], ['maximumHeapSize', 1024 ]])
print ">>>>>  AdminConfig.show(jvm)"
print AdminConfig.show(jvm)
AdminConfig.save()

連線池設定

小型(4 個 CPU)資料庫伺服器的“最佳狀態”是提供 100-200 個連線。WebSphere 作為資料庫伺服器前面的一個連線集線器。連線池的大小限制了開放多少資料庫連線來受理傳入的頁面請求。

清單 6 中的指令碼將 Trade6 應用程式中使用的 JDBC 資料來源的資料庫連線池限制設定為 113。如果您的應用程式使用所有可用的連線,則大多數連線都可能有一個需求未能滿足。您可以通過閱讀 javacore 來檢測此未滿足的需求,或者通過增加連線數並檢視什麼時候應用伺服器停止請求更多的連線。如果連線數等於或大於 WebSphere 客戶端使用者數,則應該檢查應用程式是否存在不嚴謹的程式碼。


清單 6. 將 JDBC 連線池大小設定為 113
#(c)copyright 2005
# sample code - not supported

server1=AdminConfig.getid('/Node:tux1Node01/Server:server1/')
print server1
jvm = AdminConfig.list('JavaVirtualMachine', server1)
print "-->  variable jvm is"
print jvm
print "-->  AdminConfig.show(jvm)"

myds=AdminConfig.getid('/DataSource:TradeDataSource/')
mydslist=AdminConfig.list('ConnectionPool',myds)
print "-->  before: "
print AdminConfig.show(mydslist)

AdminConfig.modify(myds, '[[connectionPool [[maxConnections 113]]]]')
AdminConfig.save()
#AdminConfig.modify(myds, '[[connectionPool [[minConnections 20]]]]')
#AdminConfig.save()
print "-->  after: "
mydslist=AdminConfig.list('ConnectionPool',myds)
print AdminConfig.show(mydslist)

# monitor connections at the database with the command
# watch -d -n 5 "db2 list applications | wc -l"
# or informix
# watch -d -n 5 "onstat -g ses | wc -l"
# this will include some irrelevant lines in count -- feel free to egrep them out

啟用 Servlet 快取

WebSphere Application Server 內建了兩個效能工具以幫助改進配置。Performance Adviser 會友好地建議您優化 servlet 快取,如果您同意,它將使效能得以改善。下面的圖 8 顯示了效能檢視器的圖形輸出。要訪問免費的 IBM 聯機 Education Assistant(它提供了有關如何使用 PMI 工具的教程),請查閱下面的參考資料


清單 7. 啟用 Servlet 快取
server1=AdminConfig.getid('/Node:tux1Node01/Server:server1/')
print server1
mywebcont=AdminConfig.list('WebContainer', server1)
print AdminConfig.show(mywebcont)
print "now modify settings"
AdminConfig.modify(mywebcont, [['enableServletCaching', 'true']] )
AdminConfig.save()
print AdminConfig.show(mywebcont)


圖 8. 效能檢視器
圖 8. 效能檢視器

執行緒池計數

此指令碼適度地增加執行緒池以保證 CPU 能夠接受。一個 CPU 可以驅動 50 到 75 Java 執行緒。該指令碼很重要,因為它必須發現與 Web 容器相關的執行緒池。一些簡單的 Jython 程式碼發現正確的識別符號,然後為活動執行緒分配新的最小和最大值:


清單 8. 增加 WebContainer 的執行緒池大小
server1=AdminConfig.getid('/Node:tux1Node01/Server:server1/')
# show all thread pools
# print AdminConfig.list('ThreadPool', server1)
# from all the ThreadPools take the WebContainer
# it will look something like this:
#webpool='WebContainer(cells/tux1Node01Cell/nodes/tux1Node01
#  cont...           /servers/server1|server.xml#ThreadPool_1113265230034)'
#
# here is how to find the thread pool with jython
#
tpList=AdminConfig.list('ThreadPool', server1).split(lineSeparator)
# now loop and find WebContainer
# the string.count() tests for a substring
# for production please add your own error handling
for tp in tpList:
        if tp.count('WebContainer') == 1:
                tpWebContainer=tp
#
# white space is significant in jython, so the un-indented line
# ends the code block
print tpWebContainer

print AdminConfig.show(tpWebContainer)

# now that we have the identifier to get to tpWebContainer 
# adjust the settings
#
AdminConfig.modify( tpWebContainer, [['maximumSize', 75 ]] )
AdminConfig.save()
AdminConfig.modify( tpWebContainer, [['minimumSize', 50 ]] )
AdminConfig.save()

print AdminConfig.show(tpWebContainer)

高階優化思想:下一步優化什麼

有很多引數可以優化——一些可以使系統變得更快,一些則會使之變得更慢。要閱讀優化佳作,請查閱下面的參考資料中的 SpecJAppServer2004 全面披露報告。

有大量建立指令碼的資源——相信您會同意使用指令碼是最好的方法。在您通過一兩次使用管理 GUI瞭解了可用的引數範圍後,將會考慮轉為使用指令碼。有關指令碼的更多資訊,請參閱下面的參考資料

結束語

本文描述瞭如何基於一些簡單的經驗來配置 WebSphere Application Server。同時讓您通過訪問下面的許多資源來獲得關於效能優化的更多資訊。

在實驗基準訓練中,單一的最大改善是什麼?優化應用程式!下面的圖 9 顯示了 Trade6 應用程式的配置頁面。切換到直接的資料庫訪問和 JMS 是效能改善的最大單一貢獻者。其他應用程式引數也提供了效能改善。該實驗示例允許通過 Web 頁面更改應用程式。您的應用程式也可以通過配置檔案來獲得類似的靈活性。


圖 9. Trade6 應用程式配置頁面:單個 CPU、0.2 秒響應時間
圖 9. Trade6 應用程式配置頁面:單個 CPU、0.2 秒響應時間

如果您達到或超越了效能目標,還應該做些什麼呢?在下面的圖 10 中,擁有 0.2 秒響應時間,看起來優化已經到頭了。此時,您可以用更多的使用者、更大的每使用者事務數,或者更復雜的事務來增強測試工具。對於許多使用者數超過計劃的情況,詳細的效能描述將使您瞭解配置平臺的容納範圍有多大。

您可能對如何羸得實驗練習和如何從示例應用程式獲得最佳效能感興趣。設計該實驗和撰寫本文的目的不是為了贏得一場競爭,而是為提高 WebSphere Application Server 效能提供一個快速指導。如果我忽略了您最喜歡的優化引數,我將很樂意在以後的文章中將它包含進來——請將您的優化建議用指令碼形式傳送給我,我的郵箱是 lurie@us.ibm.com。我期待您反饋您覺得容易優化且優化後能獲得很大效能改善的引數,並將程式碼示例傳送給我們。


圖 10. 應用程式優化提供最大收穫:在一個 CPU 上獲得 0.2 秒響應時間!
圖 10. 應用程式優化提供的最大收穫:在一個 CPU 上獲得 0.2 秒響應時間!

相關文章