引言
效能是一項功能。您必須預先設計效能,否則您以後就得重寫應用程式。那麼,有哪些好的策略可使 Active Server Pages (ASP) 應用程式效能達到最佳?
本文將介紹最佳化 ASP 應用程式和 Visual Basic® Scripting Edition (VBScript) 的訣竅。本文討論了許多陷阱。本文列出的建議已經在 http://www.microsoft.com/taiwan/ 和其它網站中進行了測試,效果十分良好。本文假設您已經對 ASP 開發,包括 VBScript 和/或 JScript、ASP 應用程式、ASP 工作階段和其它 ASP 內在物件 (Request、Response 和 Server) 有基本的了解。
通常,ASP 效能不只取決於 ASP 程式碼本身。與其在一篇文章中列出所有的資訊,我們決定在本文結尾列出了與效能有關的資源。這些連結涵蓋 ASP 和非 ASP 主題,包括 ActiveX® 資料物件 (ADO)、元件物件模型 (COM)、資料庫和 Internet Information Server (IIS) 組態。這些都是我們喜歡的一些連結 - 您一定要去看看。
訣竅 1:將經常使用的資料快取處理在 Web 伺服器上
典型的 ASP 頁面從後端資料存放區中取出資料,然後將結果轉換成超文字標記語言 (HTML)。無論資料庫的速度為何,從記憶體中取出資料一定比從後端資料存放區中取出資料快得多。從本機硬碟讀取資料通常也比從資料庫中取出資料更快。因此,您通常可以將資料快取處理在 Web 伺服器上 (存放在記憶體或磁片中),來提高效能。
rs.Open ?select StatusName, StatusID from EmployeeStatus?, _
?dsn=employees;uid=sa;pwd=;?
FetchEmploymentStatusList = rs.GetRows() ? Return data as an Array
rs.Close
Set rs = Nothing
End Function
複製代碼
對上面舉例做更進一步改進,可以將 HTML 快取為清單,而不是陣列。下面是簡單的範例:
' Get Recordset, return as HTML Option list
Function FetchEmploymentStatusList
Dim rs, fldName, s
Set rs = CreateObject("ADODB.Recordset")
rs.Open "select StatusName, StatusID from EmployeeStatus", _
"dsn=employees;uid=sa;pwd=;"
s = "<select name=""EmploymentStatus"">" & vbCrLf
Set fldName = rs.Fields("StatusName") ' ADO Field Binding
Do Until rs.EOF
' Next line violates Don't Do String Concats,
' but it's OK because we are building a cache
s = s & " <option>" & fldName & "</option>" & vbCrLf
rs.MoveNext
Loop
s = s & "</select>" & vbCrLf
rs.Close
Set rs = Nothing ' See Release Early
FetchEmploymentStatusList = s ' Return data as a String
End Function
複製代碼
「在適當的條件下」,可以將 ADO 記錄集本身快取在應用程式或工作階段領域中。有兩點要注意的:
必須將 ADO 標記為無限制執行緒
必須使用中斷連線的記錄集。
如果不能保證滿足這兩個需求,則不要快取 ADO 記錄集。在下面的「非敏捷元件」和「不要快取連線」訣竅中,我們將討論將 COM 物件存放在應用程式或工作階段領域中的危險性。
當您將資料存放在應用程式或工作階段領域時,資料將存留在那裏,直到您以程式改變它、工作階段過期,或 Web 應用程式重新啟動為止。如果資料需要更新怎麼辦?要手動強制更新應用程式資料,您可以呼叫只有管理員才可存取的 ASP 畫面來更新資料。或者,您可以透過函數定期自動更新資料。下列範例存放帶有快取資料的時間戳記,並在一段時間間隔之後更新資料。
<%
' error handing not shown...
Const UPDATE_INTERVAL = 300 ' Refresh interval, in seconds
訣竅 3:將資料和 HTML 快取在 Web 伺服器的磁碟上
有時,資料可能太多,無法都快取在記憶體中。「太多」只是說法,這要看您想消耗多少記憶體,以及需快取的項目數量和取出這些項目的頻率。在任何情況下,如果資料太多,無法都快取在記憶體中,則應考慮將資料以文字或 XML 檔案快取在 Web 伺服器的硬碟上。您可以同時將資料快取在磁碟和記憶體中,為您的網站建立最佳的快取策略。
注意當測量單一 ASP 頁面的效能時,擷取磁碟上的資料可能不一定要比從資料庫擷取資料更快。但快取會降低資料庫和網路上的負載。在高負載的情況下,這樣做可大大改善總體輸送量。當快取虛耗空間很高的查詢結果 (如多表聯結或複雜的存放程序) 或大型的結果集時,是非常有效的。與往常一樣,要測試一下幾種方案的優劣。
ASP 和 COM 提供一些建立磁碟式快取方案的工具。ADO 記錄集 Save() 和 Open() 函數儲存和載入磁碟中的記錄集。您可以使用這些方法重新編寫上面的應用程式資料快取訣竅中的程式碼範例,用檔案的 Save() 代替寫到應用程式物件中的程式碼。
以下是可以用於檔案的其它元件:
Scripting.FileSystemObject 可使您建立、讀取和寫入檔案。
Internet Explorer 隨附的 Microsoft® XML 分析程式 MSXML 支援儲存和載入 XML 文件。
LookupTable 物件 (範例,用在 MSN 上) 是從磁碟載入簡單清單的最好選擇。
最後,應考慮將資料的表示快取在磁碟上,而不是資料本身。預先轉換的 HTML 可以用 .htm 或 .asp 檔案存放在磁碟上;超連結可以直接指向這些檔案。您可以使用商用工具如 XBuilder,或 Microsoft® SQL Server#8482; Internet 發行功能將產生 HTML 的程序自動化。或者,您可以將 HTML 程式碼片斷放在 .asp 檔案中。還可以使用 FileSystemObject 從磁碟讀取 HTML 檔案,或使用 XML 盡早轉換。
訣竅 5:不要將資料庫連線快取在應用程式或工作階段物件中
快取 ADO 連線通常是一個不好的策略。如果一個連線物件存放在應用程式物件中並在所有的畫面中使用,那麼所有畫面將爭用這個連線。如果連線物件存放在 ASP 工作階段物件中,那麼將為每個使用者建立資料庫連線。這就會使連線集區的優點無效,並給 Web 伺服器和資料庫帶來不必要的負載。
工作階段最大的問題不是效能,而是調適性。工作階段不跨越幾台 Web 伺服器,一旦在一台伺服器上建立工作階段,其資料就留在那兒。這表示如果您在一個 Web 伺服器群使用工作階段,您必須設計一個策略,將每個使用者請求永遠導向到使用者工作階段所在的那台伺服器上。這被稱為將使用者「stick」在 Web 伺服器上。「Sticky 工作階段」一詞就是從這裏衍生而來的。如果 Web 伺服器當機,「stuck」使用者將遺失他們的工作階段狀態,因為工作階段不是存留到磁碟上。
建置 sticky 工作階段的策略包括硬體和軟體解決方案。諸如 Windows 2000 Advanced Server 中的網路負載平衡和 Cisco 的 Local Director 之類的解決方案都可以建置 sticky 工作階段,代價是要損失一定程度的調適性。這些解決方案是不完善的。不建議此時部署您自己的軟體解決方案 (我們過去常常使用 ISAPI 篩選器和 URL 轉換等等)。
應用程式物件也不跨越多台伺服器,如果您必須跨越 Web 伺服器群共用和更新應用程式資料,您必須使用後端資料庫。但是,唯讀應用程式資料在 Web 伺服器群中仍是有用的。
如果只是因為增加執行時間 (處理錯誤後移轉和伺服器維護),大多數關鍵工作網站至少需部署兩台 Web 伺服器。因此,在設計關鍵工作應用程式時,必須建置「sticky 工作階段」,或幹脆避免工作階段,以及任何其它將使用者狀態存放在單一 Web 伺服器上的狀態管理技術。
如果您不使用工作階段,一定要將它們關閉。您可以透過 Internet Services Manager 來為應用程式關閉工作階段 (參閱 ISM 文件)。如果決定使用工作階段,您可以採用一些方法減輕它們對效能的影響。
您可以將不需要工作階段的內容 (如說明畫面、參觀者區域等等) 移到另一個已關閉工作階段的 ASP 應用程式中。您可以逐頁為在指定頁面中不需要工作階段物件的 ASP 提供提示,使用下面放在 ASP 頁面頂部的指令:
<% @EnableSessionState=False %>
使用這一指令有一個很好的理由是工作階段會為框架組建立一個有趣的問題。ASP 保證任何時候都只執行工作階段的一個請求。這樣就確保如果瀏覽器為一個使用者請求多個畫面,一次只有一個 ASP 請求接觸工作階段,避免了當存取工作階段物件時發生的多執行緒問題。很遺憾,結果一個框架組中的所有畫面將以序列化方式顯示,一個接一個,而不是同時顯示。使用者可能必須等候很長的時間,才能看到所有的框架。該故事的寓意:如果某些框架組畫面不依靠工作階段,一定要使用 @EnableSessionState=False 指令告訴 ASP。
許多管理工作階段狀態的方法可以作為使用工作階段物件的取代辦法。對於少量的狀態 (少於 4 KB),我們通常建議使用 Cookies、QueryString 變數和隱式變數。對於更大資料量,如購物籃車,後端資料庫是最適合的選擇。有關 Web 伺服器群中的狀態管理技術的文章很多。有關詳細資訊,請參閱工作階段狀態參考資料。
訣竅 7: 將程式碼封裝在 COM 物件中
如果您有許多 VBScript 或 JScript,您可以經常將程式碼移到編譯的 COM 物件中,來改善效能。編譯過的程式碼通常比解釋的程式碼執行得更快。編譯過的 COM 物件可以透過「早期連結」存取其它 COM 物件,「早期連結」是比指令碼使用的「晚期連結」更加有效地呼叫 COM 物件的方法。
將程式碼封裝在 COM 物件中還有一些優點 (除效能之外):
COM 物件有利於將表示邏輯與業務邏輯分開。
COM 物件可以保證程式碼重複使用。
許多開發人員發現以 VB、C++ 或 Visual J++ 編寫的程式碼比 ASP 更容易除錯。
COM 物件也有缺點,包括初始開發時間和需要不同的程式設計技巧。注意封裝「少」量的 ASP 可能引起效能損失,而不是效能改進。這種情況通常在少量的 ASP 程式碼被壓縮到 COM 物件時發生。在這種情況下,建立和呼叫 COM 物件的系統虛耗空間超過編譯的程式碼的優點。應反覆地試驗以確定什麼樣的 ASP 指令碼和 COM 物件程式碼的組合產生最好的效能。注意 Microsoft 的 Windows 2000/IIS 5.0 中比 Windows NT® 4.0/IIS 4.0 在指令碼和 ADO 效能方面有了很大的改進。因此,隨著 IIS 5.0 的推出,編譯程式碼比 ASP 程式碼的效能優勢有所降低。
有關在 ASP 中使用 COM 的優點和缺點的詳細討論,參閱 ASP Component Guidelines and Programming Distributed Applications with and Microsoft Visual Basic 6.0。如果您真的要部署 COM 元件,對它們進行強度測試特別重要。事實上,理所當然應對所有的 ASP 應用程式進行強度測試。
訣竅 9:跨程序執行用效能交換可靠性
ASP 和 MTS/COM+ 兩者都有組態選項,可使您權衡可靠性和效能。當建立和部署應用程式時,應知道如何權衡兩者的效能。
ASP 選項
您可以設定 ASP 應用程式以三種方法之一執行。在 IIS 5.0 中,引入了「隔離等級」這個術語以描述這些選項。這三個隔離等級分別是低、中和高級:
低級隔離。所有 IIS 的版本都有支援,且是最快的。它在 Inetinfo.exe 中執行 ASP,Inetinfo.exe 是主要 IIS 程序。如果 ASP 應用程式崩潰,IIS 也會崩潰。(要在 IIS 4.0 下重新啟動 IIS,Web 網站管理員應使用諸如 InetMon 之類的工具監視網站,如果伺服器發生故障,應啟動批次檔以重新啟動伺服器。IIS 5.0 引入了可靠的重新啟動,能將發生故障的伺服器自動重新啟動。)
中級隔離。IIS 5.0 引入了這個新的級別,它被稱為跨程序等級,因為 ASP 在 IIS 程序之外執行。在中級隔離中,被設定為作為中級隔離執行的所有 ASP 應用程式都共用一個程序空間。這就減少在一台伺服器執行多個跨程序 ASP 應用程式所必需的程序的數量。中級隔離是 IIS 5.0 中的預設隔離級別。
高級隔離。在 IIS 4.0 和 IIS 5.0 中有支援,高級隔離也是跨程序的。如果 ASP 損壞,Web 伺服器並不會當機。下次 ASP 請求時,ASP 應用程式就會自動重新啟動。在高級隔離中,設定為作為高級隔離執行的每個 ASP 應用程式都在其自有程序空間中執行。這樣做可保護 ASP 應用程式彼此之間不相互干擾。其缺點是它要求每個 ASP 應用程式都要有一個單獨的程序。當在一台伺服器上必須執行許多應用程式時,系統虛耗空間就會大大增加。
哪個選項最好呢?在 IIS 4.0 中,跨程序執行將顯著降低效能。在 IIS 5.0 中,做了許多改進,將跨程序執行 ASP 應用程式所產生的成本降到最低限度。事實上,在絕大多數測試中,IIS 5.0 中的 ASP 跨程序應用程式比 IIS 4.0 中的同程序應用程式執行得更快。不管怎樣,同程序 (低隔離等級)在兩個平台上仍產生最好的效能。但是,如果存取率相對較低或最大輸送量較低,您就不會看到低隔離等級有多大的優點。因此,在您每一 Web 伺服器每秒鐘需要數百或成千上萬畫面時,才會覺得有必要設定低隔離等級。與往常一樣,應對多種組態進行測試並確定您樂意採取哪一種權衡方案。
注意 當您執行 ASP 跨程序應用程式時,(中級或高級隔離),它們在 NT4 中的 MTS 和在 Windows 2000 中的 COM+ 中執行。即,在 NT4 中它們在 Mtx.exe 中執行;而在 Windows 2000 中,它們在 DllHost.exe 中執行。您可以在工作管理員中看到這些程序在執行。您還可以看到 IIS 如何設定跨程序 ASP 應用程式的 MTS 套件或 COM+ 應用程式。
COM 選項
雖然與 ASP 選項不完全類似,COM 元件也有三種組態選項。COM 元件可以是「未設定的」、「設定為程式庫應用程式」,或「設定為伺服器應用程式」。「未設定的」意思是指元件沒有向 COM+ 註冊。元件將在呼叫程式的程序空間執行,那就是說,它們是「同程序」。程式庫應用程式也是同程序,但使用 COM+ 的服務,包括安全、交易和內容支援。伺服器應用程式被設定為在它們自有程序空間內執行。
您可以看到未設定的元件比程式庫應用程式略有一些優勢。您還可能看到程式庫應用程式比伺服器應用程式的效能優點更大。這是因為程式庫應用程式與 ASP 在同一程序執行,而伺服器應用程式在它們的自有程序執行。程序間的呼叫比同程序呼叫虛耗空間更大。而且,當在程序之間傳遞諸如記錄集之類的資料時,必須在兩個程序之間複製所有的資料。
陷阱!當使用 COM 伺服器應用程式時,如果您在 ASP 和 COM 之間傳遞物件,要確保物件建置「按值封送」或 MBV。建置 MBV 的物件將它們自己從一個程序複製到另一個程序。這比採用物件仍在建立者的程序中而另外一個程序反覆地呼叫建立程序以使用該物件的方法好。中斷連線的 ADO 記錄集將「按值封送」,連線的記錄集則不會。Scripting.Dictionary 不執行 MBV 並不在程序之間傳遞。最後,VB 程式編寫人員請注意:MBV 不透過傳遞參數 ByVal 獲得。MBV 由原始的元件作者建置。
怎麼辦?
如果讓我們建議一個權衡效能與可靠性的合理組態,它們應是如下的組態:
在 IIS 4.0 中,使用 ASP 低隔離等級,使用 MTS 伺服器套裝軟體。
在 IIS 5.0 上,使用 ASP 的中隔離等級,並使用 COM+ 程式庫應用程式。
這些是非常一般的原則,主機公司一般情況下以中或高隔離等級執行 ASP,而單一用途的 Web 伺服器可以以低隔離級執行。權衡各種利弊,並自己決定哪個組態更能符合您的需要。
訣竅 12:將經常使用的資料複製到指令碼變數中
當存取 ASP 中的 COM 物件時,應將經常使用的物件資料複製到指令碼變數中。這樣做就會減少 COM 方法呼叫,因為 COM 方法呼叫與存取指令碼變數相比,虛耗空間相對較大。當存取 Collection 和 Dictionary 物件時,這種技術也會減少很大的查尋。
回應緩衝有兩個啟動方法。第一種,您可以使用 Internet Services Manager 啟動整個應用程式的回應緩衝。我們建議採用這種方法,在 IIS 4.0 和 IIS 5.0 中在預設情況下為啟動新的 ASP 應用程式的回應緩衝。第二種,可以在每個 ASP 畫面的接近頂端的地方放入下面的程式碼,以啟動回應緩衝:
<% Response.Buffer = True %>
複製代碼
此行程式碼必須在任何回應資料被寫到瀏覽器之前執行 (即,在任何 HTML 出現在 ASP 指令碼之前以及使用 Response.Cookies 集合設定任何 Cookies 之前)。一般來說,最好啟動整個應用程式的回應緩衝。這樣,您就不必在每個畫面最上面寫入上述的程式碼。
Response.Flush
複製代碼
關於回應緩衝有一個常見的抱怨,就是使用者感覺到 ASP 頁面的回應速度較差 (即使整個回應時間有所改進),因為他們必須等待整個畫面產生,然後才能開始看到東西。對於執行時間長的畫面,您可以用設定 Response.Buffer = False,取消回應緩衝。但是,比較好的策略是利用 Response.Flush 方法。這種方法將 ASP 轉換的所有 HTML 送到瀏覽器。例如,在轉換有 1,000 資料列的資料表中的 100 資料列之後,ASP 可以呼叫 Response.Flush 強制將轉換的結果送到瀏覽器,這樣可以讓使用者在其餘的資料列準備好之前看到頭 100 列。這種技術可以將回應緩衝與瀏覽器逐漸顯示資料完美地結合在一起。
(注意在上面的 1,000 資料列資料表的範例中,許多瀏覽器在看到關閉 </table> 標記之前不會開始顯示資料表。檢查您的目標瀏覽器是否有支援。為避免這種情況,將資料表分成多個具有較少資料列的資料表,並在每個資料表之後呼叫 Response.Flush。較新版本的 Internet Explorer 在表格完全下載之前就開始顯示資料表,如果您指定資料表欄寬,顯示速度就會特別快,這樣做可避免強制 Internet Explorer 透過測量每個儲存格的內容寬度來計算欄寬。)
<table>
<%
For each fld in rs.Fields
Response.Write (?<th>? & fld.Name & ?</th>? & vbCrLf)
Next
While Not rs.EOF
Response.Write (?<tr>?)
For Each fld in rs.Fields %>
Response.Write(?<td>? & fld.Value & ?</td>? & vbCrLf)
Next
Response.Write ?</tr>?
Wend
%>
</table>
訣竅 19: 利用瀏覽器的驗證功能
現代的瀏覽器對如 XML、DHTML、Java 附屬應用程式和遠端資料服務等功能有進階支援。盡可能使用這些功能。所有的這些技術都可以執行用戶端驗證和資料快取,節省到 Web 伺服器的來回傳輸。如果您在執行智慧型瀏覽器,此瀏覽器就能為您進行一些驗證 (例如,在執行 POST 之前,檢查信用卡總和檢查碼是否有效)。另外,盡可能使用這一功能。透過減少用戶端/伺服器之間的來回傳輸,可降低對 Web 伺服器的負載,並能減少網路傳輸量 (雖然傳送到瀏覽器的第一個頁面可能比較大) 及伺服器存取的任何後端資源。此外,使用者將不必像往常一樣讀取新頁面,因此感覺會好一些。這樣做並不意味著您可以不進行伺服器端驗證 - 您應該一定要進行伺服器端驗證。這樣做可以防止由於某種原因 (如駭客或瀏覽器不執行用戶端驗證常式) 用戶端產生錯誤的資料。
人們已經進行了大量的工作,開發「與瀏覽器無關」的 HTML。正是由於這種憂慮,開發人員不願再使用可以改善效能、大眾化的瀏覽器功能。對於一些必須關心瀏覽器「連線」問題的真正高效能網站,一個好的策略是最佳化頁面,使其適應大眾化的瀏覽器。使用瀏覽器功能元件,可以在 ASP 中輕鬆地偵測到瀏覽器功能。Microsoft FrontPage 等工具有助於設計適合於瀏覽器和指定 HTML 版本的程式碼。若需詳細的討論資訊,請參閱 When is Better Worse?Weighing the Technology Trade-Offs。
s = ??
For i = Asc(?A?) to Asc(?Z?)
s = s & Chr(i)
Next
在第一個重複項目中,您取得了包含一個字元的字串 ?A?。在第二個重複項目中,VBScript 必須重新配置字串並將兩個字元 (?AB?) 複製到 s 中。在第三個重複項目中,它還必須再次重新配置 s 並將三個字元複製到 s 中。在 N 個 (第 26 個) 重複項目中,它必須重新配置並將 N 個字元複製到 s 中。總共就是 1+2+3+...+N,即 N*(N+1)/2 次複製。
訣竅 27:進行效能測試
正如我們在前面已經講過,效能是一項功能。如果您想要改善網站的效能,那麼要設定效能目標,然後逐步改進,直到達到目標為止。不要因為怕影響專案進展,就不進行任何效能測試。通常,在專案結束時,再作必需的結構調整已經為時太晚,因而導致您的客戶失望。將效能測試作為您日常測試的一部分來進行。您可以對個別元件分別進行效能測試,如針對 ASP 畫面或 COM 物件,或整體測試網站。
許多人使用單一瀏覽器請求畫面,來測試 Web 網站的效能。這樣做就會給您一個感覺,即網站的回應能力很好,但這樣做實際上並不能告訴您在負載下網站的效能如何。
一般情況下,要想準確地測試效能,您需要一個專用的測試環境。此環境應包括硬體,其處理器速度、處理器數量、記憶體、磁碟、網路設定等方面與生產環境的硬體相似。其次,您必須指定用戶端的工作負載:有多少同時的使用者,他們發出請求的頻率,他們點擊畫面的類型等等。如果您沒有網站實際使用情況的資料,您必須估計一下使用的情況。最後,您需要一個可以模擬預期用戶端工作負載的工具。有了這些工具,您就可以開始回答諸如「如果我有 N 個同時的使用者,那麼需要多少伺服器?」之類的問題。您還可以找出出現瓶頸的原因,並以此為目標進行最佳化。
下面列出一些不錯的 Web 強度測試工具。我們強烈建議 Microsoft Web Application Stress (WAS) Toolkit。WAS 可使您記錄測試 script,然後模擬數百或成千上萬個使用者存取 Web 伺服器。WAS 報告很多統計資料,包括每秒鐘的請求數,回應時間分布情況和錯誤計數。WAS 有豐富的用戶端介面和 Web 介面,Web 介面可以讓您進行遠端測試。