明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

28條改善 ASP 性能與外觀的技巧(1-7)

[摘要]二十八條改善 ASP 性能和外觀的技巧 Len Cardinal,高級(jí)顧問(wèn),Microsoft Consulting Services George V. Reilly Microsoft IIS Performance Lead 改編自Nancy Cluts 的文章 開(kāi)發(fā)人員技術(shù)工程師Micro...
二十八條改善 ASP 性能和外觀的技巧
Len Cardinal,高級(jí)顧問(wèn),Microsoft Consulting Services George V. Reilly
Microsoft IIS Performance Lead

改編自Nancy Cluts 的文章 開(kāi)發(fā)人員技術(shù)工程師
Microsoft Corporation
2000 年 4 月

摘要:本文介紹優(yōu)化 ASP 應(yīng)用程序和 VBScript 的技巧。

目錄
技巧 1:將經(jīng)常使用的數(shù)據(jù)緩存在 Web 服務(wù)器上
技巧 2:將經(jīng)常使用的數(shù)據(jù)緩存在 Application 或 Session 對(duì)象中
技巧 3:將數(shù)據(jù)和 HTML 緩存在 Web 服務(wù)器的磁盤(pán)上
技巧 4:避免將非敏捷的組件緩存在 Application 或 Session 對(duì)象中
技巧 5:不要將數(shù)據(jù)庫(kù)連接緩存在 Application 或 Session 對(duì)象中
技巧 6:合理地使用 Session 對(duì)象
技巧 7:將代碼封裝在 COM 對(duì)象中
引言
性能是一個(gè)特征。您必須預(yù)先設(shè)計(jì)性能,否則您以后就得重寫(xiě)應(yīng)用程序。就是說(shuō),有哪些好的策略可使 Active Server Pages (ASP) 應(yīng)用程序性能達(dá)到最佳?

本文介紹了優(yōu)化 ASP 應(yīng)用程序和 Visual Basic? Scripting Edition (VBScript) 的技巧。本文討論了許多陷阱。本文列出的建議已經(jīng)在 http://www.microsoft.com 和其它站點(diǎn)中進(jìn)行了測(cè)試,效果十分顯著。本文假定您已經(jīng)對(duì) ASP 開(kāi)發(fā),包括 VBScript 和/或 JScript、ASP Application、ASP Session 和其它 ASP 固有對(duì)象(Request、 Response 和 Server)有了基本了解。

通常,ASP 性能主要取決于 ASP 代碼本身以外的很多因素。我們不在一篇文章中羅列出所有的信息,在本文結(jié)尾處我們列出了與性能有關(guān)的資源。這些鏈接涵蓋了 ASP 和非 ASP 主題,包括 ActiveX? 數(shù)據(jù)對(duì)象 (ADO)、組件對(duì)象模型 (COM)、數(shù)據(jù)庫(kù)和 Internet Information Server (IIS) 配置。這些都是我們喜歡的一些鏈接 - 一定要去看看。

技巧 1:將經(jīng)常使用的數(shù)據(jù)緩存在 Web 服務(wù)器上
典型的 ASP 頁(yè)從后端數(shù)據(jù)存儲(chǔ)中檢索數(shù)據(jù),然后將結(jié)果轉(zhuǎn)換成超文本標(biāo)記語(yǔ)言 (HTML)。無(wú)論數(shù)據(jù)庫(kù)的速度如何,從內(nèi)存中檢索數(shù)據(jù)總要比從后端數(shù)據(jù)存儲(chǔ)中檢索數(shù)據(jù)快得多。從本地硬盤(pán)讀取數(shù)據(jù)通常也比從數(shù)據(jù)庫(kù)中檢索數(shù)據(jù)更快。因此,通?梢詫(shù)據(jù)緩存在 Web 服務(wù)器上(存儲(chǔ)在內(nèi)存或磁盤(pán)中),來(lái)提高性能。

緩存是傳統(tǒng)的以空間換取時(shí)間的做法。如果您緩存的內(nèi)容正確,那么您可以看到性能會(huì)有顯著的提高。為使緩存有效,必須保存那些經(jīng)常重復(fù)使用的數(shù)據(jù),且要重新計(jì)算這些數(shù)據(jù)需要(適度)大的開(kāi)銷(xiāo)。如果緩存的都是些陳舊的數(shù)據(jù),就會(huì)造成內(nèi)存浪費(fèi)。

不經(jīng)常發(fā)生改變的數(shù)據(jù)是很好的緩存候選數(shù)據(jù),因?yàn)槟槐負(fù)?dān)心隨著時(shí)間的遷移該數(shù)據(jù)與數(shù)據(jù)庫(kù)同步的問(wèn)題。組合框列表、引用表、DHTML 碎片、擴(kuò)展標(biāo)記語(yǔ)言 (XML) 字符串、菜單項(xiàng)和站點(diǎn)配置變量(包括數(shù)據(jù)源名稱 (DSN)、Internet 協(xié)議 (IP) 地址和 Web 路徑)都是很好的緩存候選內(nèi)容。注意您可以緩存數(shù)據(jù)的“表示”,而不緩存數(shù)據(jù)本身。如果 ASP 頁(yè)很少更改,且緩存的開(kāi)銷(xiāo)也很大(例如,整個(gè)產(chǎn)品目錄),則應(yīng)考慮事先產(chǎn)生 HTML,而不是在響應(yīng)每個(gè)請(qǐng)求時(shí)重新顯示。

應(yīng)將數(shù)據(jù)緩存在哪里,有哪些緩存策略?通常,數(shù)據(jù)緩存在 Web 服務(wù)器的內(nèi)存或磁盤(pán)中。下兩個(gè)技巧講述了這兩個(gè)方法。

技巧 2: 將經(jīng)常使用的數(shù)據(jù)緩存在 Application 或 Session 對(duì)象中
ASP Application 和 Session 對(duì)象為將數(shù)據(jù)緩存在內(nèi)存中提供了方便的容器。您可以將數(shù)據(jù)指派到 Application 和 Session 對(duì)象中,這些數(shù)據(jù)在 HTTP 調(diào)用之間保留在內(nèi)存中。Session 數(shù)據(jù)是按每個(gè)用戶分別存儲(chǔ)的,而 Application 數(shù)據(jù)則在所有用戶之間共享。

什么時(shí)候?qū)?shù)據(jù)裝載到 Application 或 Session 中呢?通常,數(shù)據(jù)是在啟動(dòng) Application 或 Session 時(shí)裝載。要在 Application 或 Session 啟動(dòng)過(guò)程中裝載數(shù)據(jù),應(yīng)將適當(dāng)?shù)拇a分別添加到 Application_OnStart () 或 Session_OnStart() 中。這些函數(shù)應(yīng)在 Global.asa 中,如果沒(méi)有,則可以添加這些函數(shù)。還可以在第一次需要時(shí)裝載該數(shù)據(jù)。為此,在 ASP 頁(yè)中添加一些代碼(或編寫(xiě)一個(gè)可重復(fù)使用的腳本函數(shù)),以檢查數(shù)據(jù)是否存在,如果不存在,就裝載數(shù)據(jù)。這是一個(gè)傳統(tǒng)的性能技術(shù),稱為“惰性計(jì)算” - 在您知道需要某一個(gè)值以前不計(jì)算該值。例如:

<%
Function GetEmploymentStatusList
Dim d
d = Application(?EmploymentStatusList?)
If d = ?? Then
' FetchEmploymentStatusList function (not shown)
' fetches data from DB, returns an Array
d = FetchEmploymentStatusList()
Application(?EmploymentStatusList?) = d
End If
GetEmploymentStatusList = d
End Function
%>


可以為所需要的每個(gè)數(shù)據(jù)塊編寫(xiě)類(lèi)似的函數(shù)。

應(yīng)以什么格式存儲(chǔ)數(shù)據(jù)?可以存儲(chǔ)任何變體類(lèi)型,因?yàn)樗心_本變量都是變體型。例如,您可以存儲(chǔ)字符串、整數(shù)或數(shù)組。通常,您將以這些變量類(lèi)型之一存儲(chǔ) ADO 記錄集的內(nèi)容。要從 ADO 記錄集獲取數(shù)據(jù),您可以手工將數(shù)據(jù)復(fù)制到 VBScript 變量,一次一個(gè)字段。使用一個(gè) ADO 記錄集持久函數(shù) GetRows()、GetString() 或 Save()(ADO 2.5),可加快速度且更容易一些。其詳細(xì)情況已超出本文所討論的范圍,但下面給出了一個(gè)函數(shù)舉例,說(shuō)明使用 GetRows() 返回記錄集數(shù)據(jù)的一個(gè)數(shù)組:

' Get Recordset, return as an Array
Function FetchEmploymentStatusList
Dim rs
Set rs = CreateObject(?ADODB.Recordset?)
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


對(duì)上面舉例做更進(jìn)一步改進(jìn),可以將 HTML 緩存為列表,而不是數(shù)組。下面是簡(jiǎn)單的示例:

' 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


在適當(dāng)?shù)臈l件下,可以將 ADO 記錄集本身緩存在 Application 或 Session 作用域中。有兩個(gè)警告:

必須將 ADO 標(biāo)記為自由線程
必須使用斷開(kāi)連接的記錄集。
如果不能保證滿足這兩個(gè)要求,則不要緩存 ADO 記錄集。在下面的“非敏捷組件”和“不要緩存連接”技巧中,我們將討論將 COM 對(duì)象存儲(chǔ)在 Application 或 Session 作用域中的危險(xiǎn)性。

當(dāng)您將數(shù)據(jù)存儲(chǔ)在 Application 或 Session 作用域時(shí),數(shù)據(jù)將保留在那里,直到您以編程方式改變它、Session 過(guò)期或 Web 應(yīng)用程序重新啟動(dòng)為止。如果數(shù)據(jù)需要更新怎么辦?要手工強(qiáng)制對(duì) Application 數(shù)據(jù)進(jìn)行更新,您可以訪問(wèn)只有管理員才可訪問(wèn)的 ASP 頁(yè)來(lái)更新數(shù)據(jù)。或者,您可以通過(guò)函數(shù)定期自動(dòng)刷新數(shù)據(jù)。下面例子存儲(chǔ)帶有緩存數(shù)據(jù)的時(shí)間戳,并隔一段時(shí)間后刷新數(shù)據(jù)。

<%
' error handing not shown...
Const UPDATE_INTERVAL = 300 ' Refresh interval, in seconds

' Function to return the employment status list
Function GetEmploymentStatusList
UpdateEmploymentStatus
GetEmploymentStatusList = Application(?EmploymentStatusList?)
End Function

' Periodically update the cached data
Sub UpdateEmploymentStatusList
Dim d, strLastUpdate
strLastUpdate = Application(?LastUpdate?)
If (strLastUpdate = ??) Or _
(UPDATE_INTERVAL < DateDiff(?s?, strLastUpdate, Now)) Then

' Note: two or more calls might get in here. This is okay and will simply
' result in a few unnecessary fetches (there is a workaround for this)

' FetchEmploymentStatusList function (not shown)
' fetches data from DB, returns an Array
d = FetchEmploymentStatusList()

' Update the Application object. Use Application.Lock()
' to ensure consistent data
Application.Lock
Application(?EmploymentStatusList?) = Events
Application(?LastUpdate?) = CStr(Now)
Application.Unlock
End If
End Sub


請(qǐng)參見(jiàn) World's Fastest ListBox with Application Data,上面還有一個(gè)例子。

要知道在 Session 或 Application 對(duì)象中緩存大的數(shù)組不是一個(gè)好的做法。在訪問(wèn)數(shù)組的任何元素之前,腳本語(yǔ)言的語(yǔ)法要求必須臨時(shí)復(fù)制整個(gè)數(shù)組。例如,如果將由字符串組成的有 100,000 個(gè)元素的數(shù)組(該數(shù)組將美國(guó)郵政編碼映射到當(dāng)?shù)氐臍庀笳荆┚彺嬖?Application 對(duì)象中,ASP 必須先將所有的 100,000 個(gè)氣象站復(fù)制到臨時(shí)數(shù)組中,然后才能提取一個(gè)字符串。在這種情況下,用自定義方法建立一個(gè)自定義組件來(lái)存儲(chǔ)氣象站 - 或使用一個(gè)詞典組件會(huì)更好。

再警告大家一下,不要將嬰兒與洗澡水一起倒掉:數(shù)組能快速查尋和存儲(chǔ)在內(nèi)存中是鄰近的關(guān)鍵數(shù)據(jù)對(duì)。索引一個(gè)詞典比索引一個(gè)數(shù)組要慢得多。應(yīng)針對(duì)您的實(shí)際情況,選擇提供最佳性能的數(shù)據(jù)結(jié)構(gòu)。

技巧 3:將數(shù)據(jù)和 HTML 緩存在 Web 服務(wù)器的磁盤(pán)上
有時(shí),數(shù)據(jù)可能太多,無(wú)法都緩存在內(nèi)存中!疤唷敝皇且粋(gè)說(shuō)法,這要看您想消耗多少內(nèi)存,以及需緩存的項(xiàng)目數(shù)和檢索這些項(xiàng)目的頻率。在任何情況下,如果數(shù)據(jù)太多而無(wú)法都緩存在內(nèi)存中,則考慮將數(shù)據(jù)以文本或 XML 文件緩存在 Web 服務(wù)器的硬盤(pán)上。可以同時(shí)將數(shù)據(jù)緩存在磁盤(pán)和內(nèi)存中,為您的站點(diǎn)建立最適宜的緩存策略。

注意當(dāng)測(cè)量單個(gè) ASP 頁(yè)的性能時(shí),檢索磁盤(pán)上的數(shù)據(jù)可能不一定要比從數(shù)據(jù)庫(kù)檢索數(shù)據(jù)更快。但緩存會(huì)降低數(shù)據(jù)庫(kù)和網(wǎng)絡(luò)上的負(fù)載。在高負(fù)載的情況下,這樣做可大大改善總體吞吐量。當(dāng)緩存開(kāi)銷(xiāo)很大的查詢結(jié)果(如多表聯(lián)接或復(fù)合存儲(chǔ)過(guò)程)或大的結(jié)果集時(shí),這是非常有效的。與往常一樣,要測(cè)試一下幾種方案的優(yōu)劣。

ASP 和 COM 提供一些建立基于磁盤(pán)的緩存方案的工具。ADO 記錄集 Save() 和 Open() 函數(shù)保存和裝載磁盤(pán)中的記錄集。可以使用這些方法重新編寫(xiě)上面 Application 數(shù)據(jù)緩存技巧中的代碼示例,用文件的 Save() 代替寫(xiě)到 Application 對(duì)象中的代碼。

有一些其它組件可以用于文件:

Scripting.FileSystemObject 可使您創(chuàng)建、讀和寫(xiě)文件。
與 Internet Explorer 一起提供的 Microsoft? XML 解析器 (MSXML) 支持保存和裝載 XML 文檔。
LookupTable 對(duì)象(例如,用在 MSN 上)是從磁盤(pán)裝載簡(jiǎn)單列表的最好選擇。
最后,應(yīng)考慮將數(shù)據(jù)的表示緩存在磁盤(pán)上,而不是數(shù)據(jù)本身。預(yù)先轉(zhuǎn)換的 HTML 可以用 .htm 或 .asp 文件存儲(chǔ)在磁盤(pán)上,超級(jí)鏈接可以直接指向這些文件?梢允褂蒙逃霉ぞ撸 XBuilder,或 Microsoft? SQL Server? Internet 發(fā)布功能將產(chǎn)生 HTML 的過(guò)程自動(dòng)化。或者,您可以將 HTML 代碼片斷放在 .asp 文件中。還可以使用 FileSystemObject 從磁盤(pán)讀取 HTML 文件,或使用 XML 盡早轉(zhuǎn)換。

技巧 4:避免將非敏捷的組件緩存在 Application 或 Session 對(duì)象中
盡管將數(shù)據(jù)緩存在 Application 或 Session 對(duì)象中是一個(gè)好的做法,但緩存 COM 對(duì)象卻有嚴(yán)重的陷阱。通常,人們傾向于將經(jīng)常使用的 COM 對(duì)象緩存到 Application 或 Session 對(duì)象中。很遺憾,許多 COM 對(duì)象(包括所有以 Visual Basic 6.0 或更低版本編寫(xiě)的對(duì)象)當(dāng)存儲(chǔ)在 Application 或 Session 對(duì)象時(shí),會(huì)引起嚴(yán)重的瓶頸。

具體來(lái)講,當(dāng)任何不敏捷的組件緩存在 Session 或 Application 對(duì)象時(shí),將引起性能瓶頸。敏捷的組件是被標(biāo)記為 ThreadingModel=Both 的組件,它聚集 Free-threaded marshaler (FTM);或被標(biāo)記為 ThreadingModel=Neutral 的組件。(Neutral 模型是 Windows? 2000 和 COM+ 的新增模型。) 下列組件不是敏捷的:

自由線程的組件(除非它們聚集 FTM)。
單元線程組件。
單線程組件。
配置的組件(Microsoft Transaction Server (MTS)/COM+ 庫(kù)和服務(wù)器程序包/應(yīng)用程序)不是敏捷的,除非它們是 Neutral 線程。單元線程組件和其它非敏捷的組件在頁(yè)作用域內(nèi)是最適合的(即,它們?cè)趩蝹(gè) ASP 頁(yè)上創(chuàng)建和銷(xiāo)毀)。

在 IIS 4.0 中,被標(biāo)記為 ThreadingModel=Both 的組件被認(rèn)為是敏捷的。在 IIS 5.0 中,只有這一點(diǎn)還不夠。組件必須不僅被標(biāo)記 Both,還必須聚集 FTM。有關(guān)敏捷性的文章講述了如何使以 Active Template Library 編寫(xiě)的 C++ 組件聚集 FTM。要注意如果組件緩存界面指針,那么那些指針本身必須是敏捷的,或必須存儲(chǔ)在 COM 共用界面表 (GIT) 中。如果您不能重新編譯 Both 線程組件以聚集 FTM,那么您可以將組件標(biāo)記為 ThreadingModel=Neutral;蛘撸绻幌胱 IIS 執(zhí)行敏捷性檢查(因此,您可以允許非敏捷的組件存儲(chǔ)在 Application 或 Session 作用域中),您可以在配置數(shù)據(jù)庫(kù)中將 AspTrackThreadingModel 設(shè)置為 True。不建議更改 AspTrackThreadingModel。

如果您想將以 Server.CreateObject 創(chuàng)建的非敏捷的組件存儲(chǔ)在 Application 對(duì)象中,IIS 5.0 將出現(xiàn)一個(gè)錯(cuò)誤。您可以在 Global.asa 中使用 <object runat=server scope=application ...> 避免這一錯(cuò)誤,但不建議這樣做,因?yàn)檫@會(huì)導(dǎo)致匯集和串行化,關(guān)于這一點(diǎn)將在下面講述。

如果您緩存非敏捷的組件會(huì)出現(xiàn)什么毛?緩存在 Session 對(duì)象中的非敏捷的組件將 Session 鎖定于 ASP 工作者線程。ASP 維護(hù)一個(gè)工作者線程池來(lái)處理請(qǐng)求。通常情況下,一個(gè)新請(qǐng)求總是由第一個(gè)可用的工作者線程來(lái)處理。如果 Session 被鎖定于一個(gè)線程,那么請(qǐng)求必須等到其相關(guān)的線程可用為止。這里有一個(gè)類(lèi)比,也許會(huì)有所幫助:您去一家超級(jí)市場(chǎng),挑選了一些商品,并在 #_3 收款臺(tái)付款。其后,每當(dāng)您在那家超級(jí)市場(chǎng)為商品付款時(shí),您總是必須在 #_3 收款臺(tái)付款,即使其它收款臺(tái)前排隊(duì)的人較少或者沒(méi)有人排隊(duì),也是如此。

將非敏捷的組件存儲(chǔ)在 Application 作用域?qū)π阅艿挠绊懮踔粮鼔。ASP 必須創(chuàng)建一個(gè)特殊的線程運(yùn)行存儲(chǔ)在 Application 作用域中的非敏捷組件。這會(huì)有兩個(gè)結(jié)果:所有調(diào)用都必須匯集到此線程,且所有調(diào)用都排成長(zhǎng)隊(duì)。“匯集”的意思是參數(shù)必須存儲(chǔ)在內(nèi)存的共享區(qū)域;執(zhí)行一個(gè)開(kāi)銷(xiāo)很大的到特殊線程的上下文切換;執(zhí)行組件的方法;將結(jié)果匯集到共享區(qū)域;執(zhí)行另一個(gè)開(kāi)銷(xiāo)很大的上下文切換,將控制返回到原始的線程!按谢币馑际侵该看沃贿\(yùn)行一個(gè)方法。兩個(gè)不同的 ASP 工作者線程不能同時(shí)在共享組件上執(zhí)行多個(gè)方法。這樣就杜絕了并發(fā)性,特別是在多處理器計(jì)算機(jī)上。更糟的是,所有非敏捷的 Application 作用域的組件共享一個(gè)線程(主機(jī) STA),因此串行化的影響甚至更顯著。

如之奈何?下面是一些一般的規(guī)則。如果您使用 Visual Basic (6.0) 或更早版本編寫(xiě)對(duì)象,那么不要將它們緩存在 Application 或 Session 對(duì)象中。如果您不知道對(duì)象的線程模型,不要緩存它。不要緩存非敏捷的對(duì)象,而應(yīng)在每個(gè)頁(yè)面創(chuàng)建和釋放它們。對(duì)象直接在 ASP 工作者線程上運(yùn)行,因此沒(méi)有匯集或串行化。如果 COM 對(duì)象在 IIS 服務(wù)器上運(yùn)行,且如果它們不花長(zhǎng)時(shí)間初始化和刪除,性能尚可。注意單線程對(duì)象不應(yīng)該這樣使用。小心 - VB 可創(chuàng)建單線程對(duì)象!如果您必須這樣使用單線程對(duì)象(如 Microsoft Excel 電子表格),別指望會(huì)有很高的吞吐量。

當(dāng) ADO 被標(biāo)記為自由線程,ADO 記錄集可以安全地緩存。要將 ADO 標(biāo)記為自由線程,使用 Makfre15.bat 文件,該文件通常位于目錄 \\Program Files\Common\System\ADO 中。

警告 如果您使用 Microsoft Access 作為數(shù)據(jù)庫(kù),不應(yīng)將 ADO 標(biāo)記為自由線程的。ADO 記錄集也必須切斷連接。一般來(lái)說(shuō),如果您不能控制站點(diǎn)中的 ADO 配置(例如,您是一個(gè)獨(dú)立的軟件廠商 [ISV],向管理他們自己的配置客戶銷(xiāo)售 Web 應(yīng)用程序),最好不要緩存記錄集。

詞典組件也是敏捷的對(duì)象。LookupTable 從數(shù)據(jù)文件中裝載其數(shù)據(jù),可用于組合框數(shù)據(jù)和配置信息。Duwamish Books 中的 PageCache 對(duì)象可提供詞典語(yǔ)法,Caprock Dictionary 也可提供。這些對(duì)象或其派生對(duì)象可以構(gòu)成有效緩存策略的基礎(chǔ)。注意 Scripting.Dictionary 對(duì)象不是敏捷的,不應(yīng)該存儲(chǔ)在 Application 或 Session 作用域中。

技巧 5:不要將數(shù)據(jù)庫(kù)連接緩存在 Application 或 Session 對(duì)象中
緩存 ADO 連接通常是很糟糕的策略。如果一個(gè) Connection 對(duì)象存儲(chǔ)在 Application 對(duì)象中,并在所有的頁(yè)面中使用,那么所有頁(yè)面將爭(zhēng)搶這一連接。如果 Connection 對(duì)象存儲(chǔ)在 ASP Session 對(duì)象中,那么將為每個(gè)用戶創(chuàng)建數(shù)據(jù)庫(kù)連接。這就會(huì)使連接池的優(yōu)勢(shì)蕩然無(wú)存,并給 Web 服務(wù)器和數(shù)據(jù)庫(kù)帶來(lái)不必要的壓力。

可以不緩存數(shù)據(jù)庫(kù)連接,而是在使用 ADO 的每個(gè) ASP 頁(yè)面中創(chuàng)建和刪除 ADO 對(duì)象。這是很有效的,因?yàn)?IIS 內(nèi)嵌了數(shù)據(jù)庫(kù)連接池。更準(zhǔn)確地說(shuō),IIS 自動(dòng)啟用 OLEDB 和 ODBC 連接池。這就能確保在每個(gè)頁(yè)面上創(chuàng)建和刪除連接將是有效的。

因?yàn)檫B接的記錄集存儲(chǔ)一個(gè)到數(shù)據(jù)庫(kù)連接的引用,所以您不應(yīng)將連接的記錄集緩存在 Application 或 Session 對(duì)象中。但是,您可以安全地緩存斷開(kāi)連接的記錄集,它們不保存到其數(shù)據(jù)連接的引用。要斷開(kāi)記錄集連接,執(zhí)行下面的兩個(gè)步驟:

Set rs = Server.CreateObject(?ADODB.RecordSet?)
rs.CursorLocation = adUseClient ' step 1

' Populate the recordset with data
rs.Open strQuery, strProv

' Now disconnect the recordset from the data provider and data source
rs.ActiveConnection = Nothing ' step 2


有關(guān)連接池的更詳細(xì)信息,可以在 ADO 和 SQL Server 參考資料中找到。

技巧 6:合理地使用 Session 對(duì)象
既然我們已經(jīng)討論了緩存在 Application 和 Session 中的優(yōu)點(diǎn),現(xiàn)在開(kāi)始討論避免使用 Session 對(duì)象的問(wèn)題。正如下面所討論的,當(dāng)與忙的站點(diǎn)一起使用時(shí),Session 有幾個(gè)缺點(diǎn)。“忙”的意思一般是指一秒鐘要求幾百頁(yè)面或成千上萬(wàn)同時(shí)用戶的站點(diǎn)。這個(gè)技巧對(duì)于必須水平擴(kuò)展的站點(diǎn) - 即,那些利用多臺(tái)服務(wù)器以處理負(fù)載或?qū)崿F(xiàn)容錯(cuò)的站點(diǎn) - 甚至更重要。對(duì)于較小的站點(diǎn),諸如 Intranet 站點(diǎn),要想實(shí)現(xiàn) Session 帶來(lái)的方,必然增大系統(tǒng)開(kāi)銷(xiāo)。

簡(jiǎn)言之,ASP 自動(dòng)為每個(gè)訪問(wèn) Web 服務(wù)器的用戶創(chuàng)建一個(gè) Session。每個(gè) Session 大約需要 10 KB 的內(nèi)存開(kāi)銷(xiāo)(最主要的是數(shù)據(jù)存儲(chǔ)在 Session 中),這就使所有的請(qǐng)求都減慢。在配置的超時(shí)時(shí)段(通常是 20 分鐘)結(jié)束以前,Session 一直保留有效。

Session 的最大的問(wèn)題不是性能,而是可擴(kuò)展性。Session 不能跨越幾臺(tái) Web 服務(wù)器,一旦在一臺(tái)服務(wù)器上創(chuàng)建 Session,其數(shù)據(jù)就留在那兒。這就意味著如果您在一個(gè) Web 服務(wù)器群使用 Session,您必須設(shè)計(jì)一個(gè)策略,將每個(gè)用戶請(qǐng)求始終發(fā)到用戶 Session 所在的那臺(tái)服務(wù)器上。這被稱為將用戶“粘”在 Web 服務(wù)器上。術(shù)語(yǔ)“粘性會(huì)話”就是從這里派生而來(lái)的。如果 Web 服務(wù)器崩潰,被“粘住的”用戶將丟失他們的會(huì)話狀態(tài),因?yàn)闀?huì)話不是粘到磁盤(pán)上。

實(shí)現(xiàn)粘性會(huì)話的策略包括硬件和軟件解決方案。諸如 Windows 2000 Advanced Server 中的網(wǎng)絡(luò)負(fù)載平衡和 Cisco 的 Local Director 之類(lèi)的解決方案都可以實(shí)現(xiàn)粘性會(huì)話,代價(jià)是要損失一定程度的可擴(kuò)展性。這些解決方案是不完善的。不建議此時(shí)部署您自己的軟件解決方案(我們過(guò)去常常使用 ISAPI 篩選器和 URL 轉(zhuǎn)換等等)。

Application 對(duì)象也不跨越多臺(tái)服務(wù)器,如果您必須跨越 Web 服務(wù)器群共享和更新 Application 數(shù)據(jù),您必須使用后端數(shù)據(jù)庫(kù)。但是,只讀 Application 數(shù)據(jù)在 Web 服務(wù)器群中仍是有用的。

如果只是因?yàn)橐黾舆\(yùn)行時(shí)間(處理故障轉(zhuǎn)移和服務(wù)器維護(hù)),大多數(shù)關(guān)鍵任務(wù)站點(diǎn)至少需部署兩臺(tái) Web 服務(wù)器。因此,在設(shè)計(jì)關(guān)鍵任務(wù)應(yīng)用程序時(shí),必須實(shí)現(xiàn)“粘性會(huì)話”,或干脆避免使用 Session,以及任何其它將用戶狀態(tài)存儲(chǔ)在單個(gè) Web 服務(wù)器上的狀態(tài)管理技術(shù)。

如果您不使用 Session,一定要將它們關(guān)閉。您可以通過(guò) Internet Services Manager,為應(yīng)用程序執(zhí)行此操作(參見(jiàn) ISM 文檔)。如果您決定使用 Session,您可以采用一些方法減輕它們對(duì)性能的影響。

您可以將不需要 Session 的內(nèi)容(如幫助屏幕,訪問(wèn)者區(qū)域等等)移到另一個(gè)關(guān)閉了 Session 的 ASP 應(yīng)用程序中。您可以逐頁(yè)提示 ASP,您不再需要該頁(yè)面上的 Session 對(duì)象,使用以下放在 ASP 頁(yè)面最上面的指令:

<% @EnableSessionState=False %>


使用這一指令有一個(gè)很好的理由是,這些 Session 在框架集方面存在一個(gè)有意思的問(wèn)題。ASP 保證任何時(shí)候 Session 只有一個(gè)請(qǐng)求執(zhí)行。這樣就確保如果瀏覽器為一個(gè)用戶請(qǐng)求多個(gè)頁(yè)面,一次只有一個(gè) ASP 請(qǐng)求接觸 Session,這樣就避免了當(dāng)訪問(wèn) Session 對(duì)象時(shí)發(fā)生的多線程問(wèn)題。很遺憾,一個(gè)框架集中的所有頁(yè)面將以串行方式顯示,一個(gè)接一個(gè),而不是同時(shí)顯示。用戶可能必須等候很長(zhǎng)時(shí)間,才能看到所有的框架。該故事的寓意:如果某些框架集頁(yè)面不依靠 Session,一定要使用 @EnableSessionState=False 指令告訴 ASP。

有許多管理 Session 狀態(tài)的方法,可替代 Session 對(duì)象的使用。對(duì)于少量的狀態(tài)(少于 4 KB),我們通常建議使用 Cookies、QueryString 變量和隱式變量。對(duì)于更大數(shù)據(jù)量,如購(gòu)物小車(chē),后端數(shù)據(jù)庫(kù)是最適合的選擇。有關(guān) Web 服務(wù)器群中狀態(tài)管理技術(shù)的文章很多。有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn) Session 狀態(tài)參考資料。

技巧 7: 將代碼封裝在 COM 對(duì)象中
如果您有許多 VBScript 或 JScript,您可以經(jīng)常將代碼移到編譯的 COM 對(duì)象中,從而可改善性能。編譯的代碼通常比解釋的代碼運(yùn)行得更快。編譯的 COM 對(duì)象可以通過(guò)“早綁定”訪問(wèn)其它 COM 對(duì)象,與腳本使用的“晚綁定”相比,“早綁定”是調(diào)用 COM 對(duì)象的更有效方法。

將代碼封裝在 COM 對(duì)象中還有一些優(yōu)點(diǎn)(除性能之外):

COM 對(duì)象有利于將表示邏輯與業(yè)務(wù)邏輯分開(kāi)。
COM 對(duì)象可以保證代碼重復(fù)使用。
許多開(kāi)發(fā)人員發(fā)現(xiàn)以 VB、C++ 或 Visual J++ 編寫(xiě)的代碼比 ASP 更容易調(diào)試。
COM 對(duì)象也有缺點(diǎn),包括初始開(kāi)發(fā)時(shí)間和需要不同的程序設(shè)計(jì)技巧。注意封裝少量的 ASP 可能引起性能下降,而不會(huì)得到性能改進(jìn)。這種情況通常在少量的 ASP 代碼被封裝進(jìn) COM 對(duì)象時(shí)發(fā)生。在這種情況下,創(chuàng)建和調(diào)用 COM 對(duì)象的系統(tǒng)開(kāi)銷(xiāo)超過(guò)了編譯的代碼的優(yōu)點(diǎn)。應(yīng)反復(fù)地試驗(yàn),以確定什么樣的 ASP 腳本和 COM 對(duì)象代碼的組合產(chǎn)生最好的性能。注意,與 Microsoft Windows NT? 4.0/IIS 4.0 相比,Windows 2000/IIS 5.0 中在腳本和 ADO 性能方面有了很大的改進(jìn)。因此,隨著 IIS 5.0 的推出,編譯代碼比 ASP 代碼的性能優(yōu)勢(shì)有所降低。

有關(guān)在 ASP 中使用 COM 的優(yōu)點(diǎn)和缺點(diǎn)的詳細(xì)討論,參見(jiàn) ASP Component Guidelines and Programming Distributed Applications with and Microsoft Visual Basic 6.0。如果您部署 COM 組件,以負(fù)荷對(duì)它們進(jìn)行測(cè)試特別重要。事實(shí)上,理所當(dāng)然應(yīng)對(duì)所有的 ASP 應(yīng)用程序進(jìn)行負(fù)荷測(cè)試。