性能是一個特性。您需要預先設計性能,或是在日后重新編寫應用程序。換句話說,什么是最大限度優化 Active Server Pages (ASP) 應用程序性能的好策略?
本文為優化 ASP 應用程序和"Visual Basic(R) 腳本編輯器 (VBScript)"提供了許多技巧。對許多陷阱和缺陷進行了討論。本文所列的建議均在 http://www.microsoft.com 及其他站點上進行了測試,而且工作正常。本文假定您對 ASP 開發有基本的理解,包括對 VBScript 和/或 JScript、ASP Application、ASP Session 和其他 ASP 內部對象(請求、響應和服務器)。
ASP 的性能,通常不止取決于 ASP 代碼本身。我們并不想在一篇文章中囊括所有的至理名言,只在最后列出與性能相關的資源。這些鏈接包括 ASP 和非 ASP 主題,包括"ActiveX(R) 數據對象 (ADO)"、"部件對象模型 (COM)"、數據庫和"Internet 信息服務器 (IIS)"配置。這些是我們喜歡的鏈接 - 務請關注它們。
技巧 1:在 Web 服務器上緩存常用數據
典型的 ASP 頁從后端數據庫檢索數據,然后將結果轉換為超文本標記語言 (HTML)。無論數據庫的速度如何,從內存檢索數據要比從后端數據庫檢索數據快得多。從本地硬盤讀取數據通常也要比從數據庫檢索數據快得多。因此,通常可以通過在 Web 服務器(在內存或磁盤)上緩存數據來改善性能。
% Function GetEmploymentStatusList Dim d d = Application("EmploymentStatusList") If d = "" Then ' FetchEmploymentStatusList 函數(不顯示) ' 從 DB 中取出數據,返回數組 d = FetchEmploymentStatusList() Application("EmploymentStatusList") = d End If GetEmploymentStatusList = d End Function %>
' 取記錄集,以數組返回 Function FetchEmploymentStatusList Dim rs Set rs = createObject("ADODB.Recordset") rs.Open "select StatusName, StatusID from EmployeeStatus", _ "dsn=employees;uid=sa;pwd=;" FetchEmploymentStatusList = rs.GetRows() ' 以數組返回數據 rs.Close Set rs = Nothing End Function
對上面示例的進一步改進應當是緩存該列表的 HTML,而不是緩存數組。下面是一個簡單的范例:
' 取記錄集,以"HTML 選項"列表返回 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 字段綁定 Do Until rs.EOF ' 下面一行違背了不要進行字符串連接, ' 但這是可以的,因為我們正在建立高速緩存 s = s " option>" fldName "/option>" vbCrLf rs.MoveNext Loop s = s "/select>" vbCrLf rs.Close Set rs = Nothing ' 參見盡早釋放 FetchEmploymentStatusList = s ' 以字符串返回數據 End Function
如果在 Application 或 Session 作用域中存儲數據,這些數據將一直保留在那兒,直到在程序中改變它、Session 過期或 Web 應用程序重新啟動時為止。數據需要更新如何處理?若要用手工強制更新應用程序數據,可以調用只允許管理員訪問的數據更新 ASP 頁。另外,還可以通過函數,周期地自動刷新數據。下面的示例存儲帶緩存數據的時間戳,在指定時間間隔后刷新數據。
' 函數返回雇傭狀態列表 Function GetEmploymentStatusList updateEmploymentStatus GetEmploymentStatusList = Application("EmploymentStatusList") End Function
' 定期更新緩存的數據 Sub updateEmploymentStatusList Dim d, strLastupdate strLastupdate = Application("Lastupdate") If (strLastupdate = "") Or _ (update_INTERVAL DateDiff("s", strLastupdate, Now)) Then
' FetchEmploymentStatusList 函數(不顯示) ' 從 DB 中取數據,返回一個數組 d = FetchEmploymentStatusList()
' 更新 Application 對象。用 Application.Lock() ' 來確保一致的數據 Application.Lock Application("EmploymentStatusList") = d Application("Lastupdate") = CStr(Now) Application.Unlock End If End Sub
有時,數據過多不能在內存中進行緩存。"過多"是一種定性的判斷;它取決于打算消耗的內存量,還有緩存項的數量和這些項的檢索頻率。總之,如果有過多的數據要在內存中緩存,請考慮以文本或 XML 文件的形式,在 Web 服務器的硬盤上緩存數據。可以將在磁盤上緩存數據和在內存中緩存數據組合起來,為站點建立最優的緩存策略。
注意,在度量單個 ASP 頁的性能時,在磁盤上檢索數據不一定比從數據庫中檢索數據快。但是,緩存減輕了數據庫和網絡的負荷。在高負荷情況下,這將明顯提高總體通信量。在查詢成本很高時緩存查詢的結果,緩存便非常有效,例如多表聯合或復雜的存儲過程,或緩存大型的結果集。按照慣例,測試競爭方案。
ASP 和 COM 提供了幾種構建磁盤緩存方案的工具。ADO 記錄集的 Save() 和 Open() 函數,保存和加載磁盤上的記錄集。您可以使用這些方法重寫上面 Application 數據緩存技巧中的范例代碼,用 Save() 文件替換向 Application 對象寫入數據的代碼。
還有其他一些處理文件的組件:
Scripting.FileSystemObject 使您能夠創建、讀取和寫入文件。 MSXML 是隨 Internet Explorer 提供的 Microsoft(R) XML 解析器,它支持保存和加載 XML 文檔。 LookupTable 對象(在 MSN 上使用的范例)是從磁盤加載簡單列表的良好選擇。 最后,請考慮在磁盤上緩存數據的表示,而不是數據本身。預制的 HTML 可以作為 .htm 或 .asp 文件存儲在磁盤上;超級鏈接可以直接指向這些文件。可以使用商業工具,如 XBuilder 或 Microsoft(R) SQL Server 的 Internet 發行功能來自動化 HTML 生成過程。另外,可以將 HTML 片段 #include 到 .asp 文件。還可以使用 FileSystemObject 從磁盤讀取 HTML 文件或使用 XML 進行早期調整(英文)。
技巧 4:避免在 Application 或 Session 對象中緩存非靈活組件
雖然在 Application 或 Session 對象中緩存數據是個好主意,但是緩存 COM 對象可能有嚴重缺陷。將常用 COM 對象嵌入 Application 或 Session 對象通常具有吸引力。遺憾的是,很多 COM 對象,包括用 Visual Basic 6.0 或更早版本編寫的 COM 對象,在 Application 或 Session 對象中存儲時將導致嚴重的瓶頸。
Session 最大的問題不是性能而是可伸縮性。Session 不能跨越 Web 服務器;一旦在一個服務器上創建了 Session,它的數據就保持在那里。這意味著,如果您在 Web 領域中使用 Sessions,您將不得不為每個用戶的請求設計一種策略,以便始終將這些請求引向用戶的 Session 所在的服務器。這被稱為將用戶"粘"到 Web 服務器上。術語"粘性會話"即來源于此。由于 Session 沒有保持到磁盤上,所以,當 Web 服務器崩潰時,被"粘住"的用戶將丟失他們的 Sessions 狀態。
用于實施粘性會話的策略包括硬件和軟件解決方案。如 Windows 2000 Advanced Server 中的網絡負載平衡解決方案和 Cisco 公司的"本地指向器"解決方案可以實施粘性會話,但以犧牲一些可伸縮性為代價。這些解決方案并不完美。我們不主張您現在全盤推翻您的軟件解決方案(我們過去常用 ISAPI 篩選器和 URL 矯直對方案進行檢查)。
Application 對象也不能跨越服務器;如果您需要在 Web 領域內共享并更新 Application 數據,則需要使用后端數據庫。但只讀的 Application 數據在 Web 領域中仍然有用。
如果只是為了增加正常運行時間(用于處理故障轉移和服務器維護),大多數執行重要任務的站點將需要部署至少兩臺 Web 服務器。所以,在設計執行重要任務的應用程序時,您將需要實施"粘性會話",或者簡單地避開 Sessions 以及其他任何在單個 Web 服務器上存儲用戶狀態的狀態管理技術。
如果當前沒有使用 Sessions,請確保將它們關閉。可以通過"Internet 服務管理器"(請參閱 ISM 文檔)來為應用程序執行該操作。如果決定使用 Sessions,可以采取幾個方法來將對性能的影響降低到最小。
可以將不需要 Sessions 的內容(如"幫助"屏幕、訪問者區域等)移動到關閉了 Sessions 的、單獨的 ASP 應用程序中。可以逐頁提示 ASP:在給定的頁中您不需要 Session 對象;使用位于 ASP 頁頂端的如下指令:
如果您有很多 VBScript 或 JScript,那么您可以通過把代碼移動到已編譯的 COM 對象來經常改進它們的性能。已編譯的代碼通常比被解釋代碼運行得更快。已編譯的 COM 對象可以通過"早期綁定"訪問其他 COM 對象,這種調用 COM 對象方法的手段,比腳本所使用的"后期綁定"更有效。
將代碼封裝在 COM 對象種有如下好處(超越性能):
COM 對象是將表達邏輯與業務邏輯分隔開來的好辦法。 COM 對象啟用了代碼重用。 很多開發商發現,用 VB、C++ 或 Visual J++ 書寫的代碼,比 ASP 更容易調試。 COM 對象有一些缺點,包括初始開發時間以及需要不同的編程技巧。需要警告您的是,封裝"少"量的 ASP 可能會導致性能降低,而不是提高。通常,在少量 ASP 代碼封裝到 COM 對象時出現這樣的情況。這時候,創建和調用 COM 對象的開銷,超過了已編譯代碼的好處。至于 ASP 腳本和 COM 對象代碼怎樣合并才能產生最佳性能還有待測試。注意,與 Windows NT(R) 4.0/IIS 4.0 相比,Microsoft 已經在 Windows 2000/IIS 5.0 中極大地提高了腳本和 ADO 性能。這樣,已編譯代碼對 ASP 代碼的性能優勢已經隨著 IIS 5.0 的引入而降低。
有關在 ASP 中使用 COM 對象的優缺點的更多討論,請參閱 ASP 組件準則和用 COM 和 Microsoft Visual Basic 6.0 對分布式應用程序進行編程(英文)。如果您的確部署了 COM 組件,要對它們進行強度測試是非常重要的。實際上,所有 ASP 應用程序都應當作為正式過程進行強度測試。
技巧 8:晚點獲取資源,早點釋放資源
這是個小技巧。通常,最好晚點獲取資源而要早點釋放資源。這些資源包括 COM 對象、文件句柄和其他資源。