改善 ASP 性能和外觀的十六個(gè)技巧
引言
性能是一個(gè)特征。您必須預(yù)先設(shè)計(jì)性能,否則您以后就得重寫應(yīng)用程序。就是說,有哪些好的策略可使 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 開發(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ù)庫和 Internet Information Server (IIS) 配置。這些都是我們喜歡的一些鏈接 - 一定要去看看。
技巧 1:將經(jīng)常使用的數(shù)據(jù)緩存在 Web 服務(wù)器上
典型的 ASP 頁從后端數(shù)據(jù)存儲(chǔ)中檢索數(shù)據(jù),然后將結(jié)果轉(zhuǎn)換成超文本標(biāo)記語言 (HTML)。無論數(shù)據(jù)庫的速度如何,從內(nèi)存中檢索數(shù)據(jù)總要比從后端數(shù)據(jù)存儲(chǔ)中檢索數(shù)據(jù)快得多。從本地硬盤讀取數(shù)據(jù)通常也比從數(shù)據(jù)庫中檢索數(shù)據(jù)更快。因此,通??梢詫?shù)據(jù)緩存在 Web 服務(wù)器上(存儲(chǔ)在內(nèi)存或磁盤中),來提高性能。
緩存是傳統(tǒng)的以空間換取時(shí)間的做法。如果您緩存的內(nèi)容正確,那么您可以看到性能會(huì)有顯著的提高。為使緩存有效,必須保存那些經(jīng)常重復(fù)使用的數(shù)據(jù),且要重新計(jì)算這些數(shù)據(jù)需要(適度)大的開銷。如果緩存的都是些陳舊的數(shù)據(jù),就會(huì)造成內(nèi)存浪費(fèi)。
不經(jīng)常發(fā)生改變的數(shù)據(jù)是很好的緩存候選數(shù)據(jù),因?yàn)槟槐負(fù)?dān)心隨著時(shí)間的遷移該數(shù)據(jù)與數(shù)據(jù)庫同步的問題。組合框列表、引用表、DHTML 碎片、擴(kuò)展標(biāo)記語言 (XML) 字符串、菜單項(xiàng)和站點(diǎn)配置變量(包括數(shù)據(jù)源名稱 (DSN)、Internet 協(xié)議 (IP) 地址和 Web 路徑)都是很好的緩存候選內(nèi)容。注意您可以緩存數(shù)據(jù)的“表示”,而不緩存數(shù)據(jù)本身。如果 ASP 頁很少更改,且緩存的開銷也很大(例如,整個(gè)產(chǎn)品目錄),則應(yīng)考慮事先產(chǎn)生 HTML,而不是在響應(yīng)每個(gè)請(qǐng)求時(shí)重新顯示。
應(yīng)將數(shù)據(jù)緩存在哪里,有哪些緩存策略?通常,數(shù)據(jù)緩存在 Web 服務(wù)器的內(nèi)存或磁盤中。下兩個(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)過程中裝載數(shù)據(jù),應(yīng)將適當(dāng)?shù)拇a分別添加到 Application_OnStart() 或 Session_OnStart() 中。這些函數(shù)應(yīng)在 Global.asa 中,如果沒有,則可以添加這些函數(shù)。還可以在第一次需要時(shí)裝載該數(shù)據(jù)。為此,在 ASP 頁中添加一些代碼(或編寫一個(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ù)塊編寫類似的函數(shù)。
應(yīng)以什么格式存儲(chǔ)數(shù)據(jù)?可以存儲(chǔ)任何變體類型,因?yàn)樗心_本變量都是變體型。例如,您可以存儲(chǔ)字符串、整數(shù)或數(shù)組。通常,您將以這些變量類型之一存儲(chǔ) ADO 記錄集的內(nèi)容。要從 ADO 記錄集獲取數(shù)據(jù),您可以手工將數(shù)據(jù)復(fù)制到 VBScript 變量,一次一個(gè)字段。使用一個(gè) ADO 記錄集持久函數(shù) GetRows()、GetString() 或 Save()(ADO 2.5),可加快速度且更容易一些。其詳細(xì)情況已超出本文所討論的范圍,但下面給出了一個(gè)函數(shù)舉例,說明使用 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ù)組。下面是簡單的示例:
' 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 = ?? & 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)記為自由線程 必須使用斷開連接的記錄集。
如果不能保證滿足這兩個(gè)要求,則不要緩存 ADO 記錄集。在下面的“非敏捷組件”和“不要緩存連接”技巧中,我們將討論將 COM 對(duì)象存儲(chǔ)在 Application 或 Session 作用域中的危險(xiǎn)性。
當(dāng)您將數(shù)據(jù)存儲(chǔ)在 Application 或 Session 作用域時(shí),數(shù)據(jù)將保留在那里,直到您以編程方式改變它、Session 過期或 Web 應(yīng)用程序重新啟動(dòng)為止。如果數(shù)據(jù)需要更新怎么辦?要手工強(qiáng)制對(duì) Application 數(shù)據(jù)進(jìn)行更新,您可以訪問只有管理員才可訪問的 ASP 頁來更新數(shù)據(jù)?;蛘?,您可以通過函數(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)參見 World's Fastest ListBox with Application Data,上面還有一個(gè)例子。
要知道在 Session 或 Application 對(duì)象中緩存大的數(shù)組不是一個(gè)好的做法。在訪問數(shù)組的任何元素之前,腳本語言的語法要求必須臨時(shí)復(fù)制整個(gè)數(shù)組。例如,如果將由字符串組成的有 100,000 個(gè)元素的數(shù)組(該數(shù)組將美國郵政編碼映射到當(dāng)?shù)氐臍庀笳荆┚彺嬖?Application 對(duì)象中,ASP 必須先將所有的 100,000 個(gè)氣象站復(fù)制到臨時(shí)數(shù)組中,然后才能提取一個(gè)字符串。在這種情況下,用自定義方法建立一個(gè)自定義組件來存儲(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ù)器的磁盤上
有時(shí),數(shù)據(jù)可能太多,無法都緩存在內(nèi)存中?!疤唷敝皇且粋€(gè)說法,這要看您想消耗多少內(nèi)存,以及需緩存的項(xiàng)目數(shù)和檢索這些項(xiàng)目的頻率。在任何情況下,如果數(shù)據(jù)太多而無法都緩存在內(nèi)存中,則考慮將數(shù)據(jù)以文本或 XML 文件緩存在 Web 服務(wù)器的硬盤上??梢酝瑫r(shí)將數(shù)據(jù)緩存在磁盤和內(nèi)存中,為您的站點(diǎn)建立最適宜的緩存策略。
注意當(dāng)測(cè)量單個(gè) ASP 頁的性能時(shí),檢索磁盤上的數(shù)據(jù)可能不一定要比從數(shù)據(jù)庫檢索數(shù)據(jù)更快。但緩存會(huì)降低數(shù)據(jù)庫和網(wǎng)絡(luò)上的負(fù)載。在高負(fù)載的情況下,這樣做可大大改善總體吞吐量。當(dāng)緩存開銷很大的查詢結(jié)果(如多表聯(lián)接或復(fù)合存儲(chǔ)過程)或大的結(jié)果集時(shí),這是非常有效的。與往常一樣,要測(cè)試一下幾種方案的優(yōu)劣。
ASP 和 COM 提供一些建立基于磁盤的緩存方案的工具。ADO 記錄集 Save() 和 Open() 函數(shù)保存和裝載磁盤中的記錄集??梢允褂眠@些方法重新編寫上面 Application 數(shù)據(jù)緩存技巧中的代碼示例,用文件的 Save() 代替寫到 Application 對(duì)象中的代碼。
有一些其它組件可以用于文件:
Scripting.FileSystemObject 可使您創(chuàng)建、讀和寫文件。 與 Internet Explorer 一起提供的 Microsoft? XML 解析器 (MSXML) 支持保存和裝載 XML 文檔。 LookupTable 對(duì)象(例如,用在 MSN 上)是從磁盤裝載簡單列表的最好選擇。
最后,應(yīng)考慮將數(shù)據(jù)的表示緩存在磁盤上,而不是數(shù)據(jù)本身。預(yù)先轉(zhuǎn)換的 HTML 可以用 .htm 或 .asp 文件存儲(chǔ)在磁盤上,超級(jí)鏈接可以直接指向這些文件??梢允褂蒙逃霉ぞ撸?XBuilder,或 Microsoft? SQL Server? Internet 發(fā)布功能將產(chǎn)生 HTML 的過程自動(dòng)化?;蛘?,您可以將 HTML 代碼片斷放在 .asp 文件中。還可以使用 FileSystemObject 從磁盤讀取 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 或更低版本編寫的對(duì)象)當(dāng)存儲(chǔ)在 Application 或 Session 對(duì)象時(shí),會(huì)引起嚴(yán)重的瓶頸。
具體來講,當(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+ 庫和服務(wù)器程序包/應(yīng)用程序)不是敏捷的,除非它們是 Neutral 線程。單元線程組件和其它非敏捷的組件在頁作用域內(nèi)是最適合的(即,它們?cè)趩蝹€(gè) ASP 頁上創(chuàng)建和銷毀)。
在 IIS 4.0 中,被標(biāo)記為 ThreadingModel=Both 的組件被認(rèn)為是敏捷的。在 IIS 5.0 中,只有這一點(diǎn)還不夠。組件必須不僅被標(biāo)記 Both,還必須聚集 FTM。有關(guān)敏捷性的文章講述了如何使以 Active Template Library 編寫的 C++ 組件聚集 FTM。要注意如果組件緩存界面指針,那么那些指針本身必須是敏捷的,或必須存儲(chǔ)在 COM 共用界面表 (GIT) 中。如果您不能重新編譯 Both 線程組件以聚集 FTM,那么您可以將組件標(biāo)記為 ThreadingModel=Neutral?;蛘?,如果您不想讓 IIS 執(zhí)行敏捷性檢查(因此,您可以允許非敏捷的組件存儲(chǔ)在 Application 或 Session 作用域中),您可以在配置數(shù)據(jù)庫中將 AspTrackThreadingModel 設(shè)置為 True。不建議更改 AspTrackThreadingModel。
如果您想將以 Server.CreateObject 創(chuàng)建的非敏捷的組件存儲(chǔ)在 Application 對(duì)象中,IIS 5.0 將出現(xiàn)一個(gè)錯(cuò)誤。您可以在 Global.asa 中使用
如果您緩存非敏捷的組件會(huì)出現(xiàn)什么毛病?緩存在 Session 對(duì)象中的非敏捷的組件將 Session 鎖定于 ASP 工作者線程。ASP 維護(hù)一個(gè)工作者線程池來處理請(qǐng)求。通常情況下,一個(gè)新請(qǐng)求總是由第一個(gè)可用的工作者線程來處理。如果 Session 被鎖定于一個(gè)線程,那么請(qǐng)求必須等到其相關(guān)的線程可用為止。這里有一個(gè)類比,也許會(huì)有所幫助:您去一家超級(jí)市場(chǎng),挑選了一些商品,并在 #_3 收款臺(tái)付款。其后,每當(dāng)您在那家超級(jí)市場(chǎng)為商品付款時(shí),您總是必須在 #_3 收款臺(tái)付款,即使其它收款臺(tái)前排隊(duì)的人較少或者沒有人排隊(duì),也是如此。
將非敏捷的組件存儲(chǔ)在 Application 作用域?qū)π阅艿挠绊懮踔粮鼔?。ASP 必須創(chuàng)建一個(gè)特殊的線程運(yùn)行存儲(chǔ)在 Application 作用域中的非敏捷組件。這會(huì)有兩個(gè)結(jié)果:所有調(diào)用都必須匯集到此線程,且所有調(diào)用都排成長隊(duì)?!皡R集”的意思是參數(shù)必須存儲(chǔ)在內(nèi)存的共享區(qū)域;執(zhí)行一個(gè)開銷很大的到特殊線程的上下文切換;執(zhí)行組件的方法;將結(jié)果匯集到共享區(qū)域;執(zhí)行另一個(gè)開銷很大的上下文切換,將控制返回到原始的線程。“串行化”意思是指每次只運(yùn)行一個(gè)方法。兩個(gè)不同的 ASP 工作者線程不能同時(shí)在共享組件上執(zhí)行多個(gè)方法。這樣就杜絕了并發(fā)性,特別是在多處理器計(jì)算機(jī)上。更糟的是,所有非敏捷的 Application 作用域的組件共享一個(gè)線程(主機(jī) STA),因此串行化的影響甚至更顯著。
如之奈何?下面是一些一般的規(guī)則。如果您使用 Visual Basic (6.0) 或更早版本編寫對(duì)象,那么不要將它們緩存在 Application 或 Session 對(duì)象中。如果您不知道對(duì)象的線程模型,不要緩存它。不要緩存非敏捷的對(duì)象,而應(yīng)在每個(gè)頁面創(chuàng)建和釋放它們。對(duì)象直接在 ASP 工作者線程上運(yùn)行,因此沒有匯集或串行化。如果 COM 對(duì)象在 IIS 服務(wù)器上運(yùn)行,且如果它們不花長時(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ù)庫,不應(yīng)將 ADO 標(biāo)記為自由線程的。ADO 記錄集也必須切斷連接。一般來說,如果您不能控制站點(diǎn)中的 ADO 配置(例如,您是一個(gè)獨(dú)立的軟件廠商 [ISV],向管理他們自己的配置客戶銷售 Web 應(yīng)用程序),最好不要緩存記錄集。
詞典組件也是敏捷的對(duì)象。LookupTable 從數(shù)據(jù)文件中裝載其數(shù)據(jù),可用于組合框數(shù)據(jù)和配置信息。Duwamish Books 中的 PageCache 對(duì)象可提供詞典語法,Caprock Dictionary 也可提供。這些對(duì)象或其派生對(duì)象可以構(gòu)成有效緩存策略的基礎(chǔ)。注意 Scripting.Dictionary 對(duì)象不是敏捷的,不應(yīng)該存儲(chǔ)在 Application 或 Session 作用域中。
技巧 5:不要將數(shù)據(jù)庫連接緩存在 Application 或 Session 對(duì)象中
緩存 ADO 連接通常是很糟糕的策略。如果一個(gè) Connection 對(duì)象存儲(chǔ)在 Application 對(duì)象中,并在所有的頁面中使用,那么所有頁面將爭搶這一連接。如果 Connection 對(duì)象存儲(chǔ)在 ASP Session 對(duì)象中,那么將為每個(gè)用戶創(chuàng)建數(shù)據(jù)庫連接。這就會(huì)使連接池的優(yōu)勢(shì)蕩然無存,并給 Web 服務(wù)器和數(shù)據(jù)庫帶來不必要的壓力。
可以不緩存數(shù)據(jù)庫連接,而是在使用 ADO 的每個(gè) ASP 頁面中創(chuàng)建和刪除 ADO 對(duì)象。這是很有效的,因?yàn)?IIS 內(nèi)嵌了數(shù)據(jù)庫連接池。更準(zhǔn)確地說,IIS 自動(dòng)啟用 OLEDB 和 ODBC 連接池。這就能確保在每個(gè)頁面上創(chuàng)建和刪除連接將是有效的。
因?yàn)檫B接的記錄集存儲(chǔ)一個(gè)到數(shù)據(jù)庫連接的引用,所以您不應(yīng)將連接的記錄集緩存在 Application 或 Session 對(duì)象中。但是,您可以安全地緩存斷開連接的記錄集,它們不保存到其數(shù)據(jù)連接的引用。要斷開記錄集連接,執(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)在開始討論避免使用 Session 對(duì)象的問題。正如下面所討論的,當(dāng)與忙的站點(diǎn)一起使用時(shí),Session 有幾個(gè)缺點(diǎn)?!懊Α钡囊馑家话闶侵敢幻腌娨髱装夙撁婊虺汕先f同時(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 帶來的方,必然增大系統(tǒng)開銷。
簡言之,ASP 自動(dòng)為每個(gè)訪問 Web 服務(wù)器的用戶創(chuàng)建一個(gè) Session。每個(gè) Session 大約需要 10 KB 的內(nèi)存開銷(最主要的是數(shù)據(jù)存儲(chǔ)在 Session 中),這就使所有的請(qǐng)求都減慢。在配置的超時(shí)時(shí)段(通常是 20 分鐘)結(jié)束以前,Session 一直保留有效。
Session 的最大的問題不是性能,而是可擴(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ù)語“粘性會(huì)話”就是從這里派生而來的。如果 Web 服務(wù)器崩潰,被“粘住的”用戶將丟失他們的會(huì)話狀態(tài),因?yàn)闀?huì)話不是粘到磁盤上。
實(shí)現(xiàn)粘性會(huì)話的策略包括硬件和軟件解決方案。諸如 Windows 2000 Advanced Server 中的網(wǎng)絡(luò)負(fù)載平衡和 Cisco 的 Local Director 之類的解決方案都可以實(shí)現(xiàn)粘性會(huì)話,代價(jià)是要損失一定程度的可擴(kuò)展性。這些解決方案是不完善的。不建議此時(shí)部署您自己的軟件解決方案(我們過去常常使用 ISAPI 篩選器和 URL 轉(zhuǎn)換等等)。
Application 對(duì)象也不跨越多臺(tái)服務(wù)器,如果您必須跨越 Web 服務(wù)器群共享和更新 Application 數(shù)據(jù),您必須使用后端數(shù)據(jù)庫。但是,只讀 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)閉。您可以通過 Internet Services Manager,為應(yīng)用程序執(zhí)行此操作(參見 ISM 文檔)。如果您決定使用 Session,您可以采用一些方法減輕它們對(duì)性能的影響。
您可以將不需要 Session 的內(nèi)容(如幫助屏幕,訪問者區(qū)域等等)移到另一個(gè)關(guān)閉了 Session 的 ASP 應(yīng)用程序中。您可以逐頁提示 ASP,您不再需要該頁面上的 Session 對(duì)象,使用以下放在 ASP 頁面最上面的指令:
使用這一指令有一個(gè)很好的理由是,這些 Session 在框架集方面存在一個(gè)有意思的問題。ASP 保證任何時(shí)候 Session 只有一個(gè)請(qǐng)求執(zhí)行。這樣就確保如果瀏覽器為一個(gè)用戶請(qǐng)求多個(gè)頁面,一次只有一個(gè) ASP 請(qǐng)求接觸 Session,這樣就避免了當(dāng)訪問 Session 對(duì)象時(shí)發(fā)生的多線程問題。很遺憾,一個(gè)框架集中的所有頁面將以串行方式顯示,一個(gè)接一個(gè),而不是同時(shí)顯示。用戶可能必須等候很長時(shí)間,才能看到所有的框架。該故事的寓意:如果某些框架集頁面不依靠 Session,一定要使用 @EnableSessionState=False 指令告訴 ASP。
有許多管理 Session 狀態(tài)的方法,可替代 Session 對(duì)象的使用。對(duì)于少量的狀態(tài)(少于 4 KB),我們通常建議使用 Cookies、QueryString 變量和隱式變量。對(duì)于更大數(shù)據(jù)量,如購物小車,后端數(shù)據(jù)庫是最適合的選擇。有關(guān) Web 服務(wù)器群中狀態(tài)管理技術(shù)的文章很多。有關(guān)詳細(xì)信息,請(qǐng)參見 Session 狀態(tài)參考資料。
技巧 7: 將代碼封裝在 COM 對(duì)象中
如果您有許多 VBScript 或 JScript,您可以經(jīng)常將代碼移到編譯的 COM 對(duì)象中,從而可改善性能。編譯的代碼通常比解釋的代碼運(yùn)行得更快。編譯的 COM 對(duì)象可以通過“早綁定”訪問其它 COM 對(duì)象,與腳本使用的“晚綁定”相比,“早綁定”是調(diào)用 COM 對(duì)象的更有效方法。
將代碼封裝在 COM 對(duì)象中還有一些優(yōu)點(diǎn)(除性能之外):
COM 對(duì)象有利于將表示邏輯與業(yè)務(wù)邏輯分開。 COM 對(duì)象可以保證代碼重復(fù)使用。 許多開發(fā)人員發(fā)現(xiàn)以 VB、C++ 或 Visual J++ 編寫的代碼比 ASP 更容易調(diào)試。
COM 對(duì)象也有缺點(diǎn),包括初始開發(fā)時(shí)間和需要不同的程序設(shè)計(jì)技巧。注意封裝少量的 ASP 可能引起性能下降,而不會(huì)得到性能改進(jìn)。這種情況通常在少量的 ASP 代碼被封裝進(jìn) COM 對(duì)象時(shí)發(fā)生。在這種情況下,創(chuàng)建和調(diào)用 COM 對(duì)象的系統(tǒng)開銷超過了編譯的代碼的優(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ì)討論,參見 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è)試。
技巧 8:遲一點(diǎn)獲得資源,早一點(diǎn)釋放資源
這里是一個(gè)小技巧供您參考。一般來說,最好遲一點(diǎn)獲得資源,早一點(diǎn)釋放資源。這適用于 COM 對(duì)象以及文件句柄和其它資源。
這種優(yōu)化方法主要用于 ADO 連接和記錄集。當(dāng)您使用完記錄集,比方說在顯示一個(gè)表及其數(shù)據(jù)之后,應(yīng)立即釋放它,而不是等到頁面結(jié)束時(shí)再釋放。將 VBScript 變量設(shè)置為 Nothing 是最好的做法。不要讓記錄集超出作用域之外。而且,要釋放任何相關(guān)的 Command 或 Connection 對(duì)象(在將記錄集或連接設(shè)置為 = Nothing 之前,不要忘記調(diào)用 Close())。這會(huì)縮短數(shù)據(jù)庫必須為您準(zhǔn)備資源的時(shí)間,并盡快釋放數(shù)據(jù)庫到連接池的連接。
技巧 9:進(jìn)程外執(zhí)行過程以性能換取可靠性
ASP 和 MTS/COM+ 兩者都有配置選項(xiàng),可使您兼顧可靠性和性能。當(dāng)建立和部署應(yīng)用程序時(shí),應(yīng)知道如何兼顧兩者的性能。
ASP 選項(xiàng)
可以配置 ASP 應(yīng)用程序,以便以三種方法之一運(yùn)行。在 IIS 5.0 中,引入了“隔離級(jí)”這一術(shù)語以說明這些選項(xiàng)。這三個(gè)隔離級(jí)分別是低級(jí)、中級(jí)和高級(jí):
低級(jí)隔離。這在 IIS 的所有版本中都得到支持,且是最快的。它在 Inetinfo.exe 中運(yùn)行 ASP,Inetinfo.exe 是主要 IIS 進(jìn)程。如果 ASP 應(yīng)用程序崩潰,IIS 也會(huì)崩潰。(要在 IIS 4.0 下重新啟動(dòng) IIS,Web 站點(diǎn)管理員應(yīng)使用諸如 InetMon 之類的工具監(jiān)視站點(diǎn),如果服務(wù)器發(fā)生故障,應(yīng)啟用批處理文件以重新啟動(dòng)服務(wù)器。IIS 5.0 引入了可靠的重新啟動(dòng),該方法可使發(fā)生故障的服務(wù)器自動(dòng)重新啟動(dòng)。) 中級(jí)隔離。IIS 5.0 引入了這個(gè)新的級(jí)別,它被稱為進(jìn)程外級(jí)別,因?yàn)?ASP 在 IIS 進(jìn)程之外運(yùn)行。在中級(jí)隔離中,被配置作為中級(jí)隔離運(yùn)行的所有 ASP 應(yīng)用程序都共享一個(gè)進(jìn)程空間。這就減少了在一臺(tái)服務(wù)器運(yùn)行多個(gè)進(jìn)程外 ASP 應(yīng)用程序所需要的進(jìn)程數(shù)量。中級(jí)隔離是 IIS 5.0 中的默認(rèn)隔離級(jí)別。 高級(jí)隔離。在 IIS 4.0 和 IIS 5.0 中支持這一級(jí)別,高級(jí)隔離也是進(jìn)程外的。如果 ASP 崩潰,Web 服務(wù)器并不會(huì)崩潰。下次 ASP 請(qǐng)求時(shí),ASP 應(yīng)用程序就會(huì)自動(dòng)重新啟動(dòng)。在高級(jí)隔離中,配置作為高級(jí)隔離運(yùn)行的每個(gè) ASP 應(yīng)用程序都在其自有進(jìn)程空間中運(yùn)行。這樣做可保護(hù) ASP 應(yīng)用程序彼此之間不相互干擾。其缺點(diǎn)是它要求每個(gè) ASP 應(yīng)用程序都要有一個(gè)單獨(dú)的進(jìn)程。當(dāng)在一臺(tái)服務(wù)器上必須運(yùn)行許多應(yīng)用程序時(shí),系統(tǒng)開銷就會(huì)大大增加。
哪個(gè)選項(xiàng)最好的呢?在 IIS 4.0 中,進(jìn)程外運(yùn)行將顯著降低性能。在 IIS 5.0 中,做了許多改進(jìn),將進(jìn)程外運(yùn)行 ASP 應(yīng)用程序所產(chǎn)生的開銷降到最低限度。事實(shí)上,在絕大多數(shù)測(cè)試中,IIS 5.0 中的 ASP 進(jìn)程外應(yīng)用程序比 IIS 4.0 中的進(jìn)程內(nèi)應(yīng)用程序運(yùn)行得更快。不管怎樣,在兩個(gè)平臺(tái)上,進(jìn)程內(nèi)(低隔離級(jí))性能最佳。但是,如果訪問率相對(duì)較低或最大吞吐量較低,低隔離級(jí)的優(yōu)勢(shì)不太明顯。因此,在您每一 Web 服務(wù)器每秒鐘需要數(shù)百或成千上萬頁面時(shí),才會(huì)覺得有必要設(shè)置低隔離級(jí)。與往常一樣,應(yīng)對(duì)多種配置進(jìn)行測(cè)試,確定您要采取哪一種折衷方案。
注意 當(dāng)您運(yùn)行 ASP 進(jìn)程外應(yīng)用程序時(shí)(中級(jí)或高級(jí)隔離),它們?cè)?NT4 中的 MTS 和在 Windows 2000 中的 COM+ 中運(yùn)行。即,在 NT4 中它們?cè)?Mtx.exe 中運(yùn)行;而在 Windows 2000 中,它們?cè)?DllHost.exe 中運(yùn)行。您可以在任務(wù)管理器中看到這些進(jìn)程在運(yùn)行。您還可以看到 IIS 如何為進(jìn)程外 ASP 應(yīng)用程序配置 MTS 程序包或 COM+ 應(yīng)用程序。
COM 選項(xiàng)
COM 組件也有三種配置選項(xiàng),雖然與 ASP 選項(xiàng)不完全類似。COM 組件可以是“未配置的”、配置為庫應(yīng)用程序或配置為服務(wù)器應(yīng)用程序?!拔磁渲玫摹币馑际侵附M件沒有注冊(cè) COM+。組件將在調(diào)用程序的進(jìn)程空間運(yùn)行,那就是說,它們是“進(jìn)程內(nèi)的”。庫應(yīng)用程序也是進(jìn)程內(nèi)的,但使用 COM+ 的服務(wù),包括安全、事務(wù)和上下文支持。服務(wù)器應(yīng)用程序被配置為在它們自有的進(jìn)程空間內(nèi)運(yùn)行。
您可以看到未配置的組件比庫應(yīng)用程序略有一些優(yōu)勢(shì)。庫應(yīng)用程序比服務(wù)器應(yīng)用程序的性能優(yōu)點(diǎn)更大。這是因?yàn)閹鞈?yīng)用程序與 ASP 在同一進(jìn)程內(nèi)運(yùn)行,而服務(wù)器應(yīng)用程序在它們的自有進(jìn)程內(nèi)運(yùn)行。進(jìn)程間的調(diào)用比進(jìn)程內(nèi)調(diào)用開銷更大。而且,當(dāng)在進(jìn)程之間傳遞諸如記錄集之類的數(shù)據(jù)時(shí),必須在兩個(gè)進(jìn)程之間復(fù)制所有的數(shù)據(jù)。
陷阱!當(dāng)使用 COM 服務(wù)器應(yīng)用程序時(shí),如果您在 ASP 和 COM 之間傳遞對(duì)象,要確保對(duì)象執(zhí)行“按值匯集”或 MBV。執(zhí)行 MBV 的對(duì)象將它們自己從一個(gè)進(jìn)程復(fù)制到另一個(gè)進(jìn)程。這比下面一種方法好,采用這種方法時(shí),對(duì)象仍在創(chuàng)建者的進(jìn)程中,另外一個(gè)進(jìn)程反復(fù)地調(diào)用創(chuàng)建進(jìn)程以使用該對(duì)象。切斷連接的 ADO 記錄集將“按值匯集”,連接的記錄集則不然。Scripting.Dictionary 不執(zhí)行 MBV,且不在進(jìn)程之間傳遞。最后,VB 程序員請(qǐng)注意:MBV 不通過傳遞參數(shù) ByVal 獲得。MBV 由原始的組件作者執(zhí)行。
怎么辦?
如果讓我們建議一個(gè)兼顧性能與可靠性的合理配置,它們應(yīng)是如下的配置:
在 IIS 4.0 中,使用 ASP 低隔離級(jí)別,使用 MTS 服務(wù)器程序包。 在 IIS 5.0 上,使用 ASP 的中隔離級(jí),并使用 COM+ 庫應(yīng)用程序。
這些是非常一般的原則,主機(jī)服務(wù)公司一般情況下以中或高隔離級(jí)運(yùn)行 ASP,而單用途的 Web 服務(wù)器可以以低隔離級(jí)運(yùn)行。衡量各種利弊,并自己決定哪個(gè)配置更能符合您的需要。
技巧 10:使用顯式選項(xiàng)
在 .asp 文件中應(yīng)使用 Option Explicit。此指令放在 .asp 文件的最上面,它強(qiáng)制開發(fā)人員聲明要使用到的所有變量。許多程序員認(rèn)為這種方法對(duì)于調(diào)試應(yīng)用程序很有幫助,因?yàn)檫@種方法避免了鍵錯(cuò)變量名和誤建新變量的可能性(例如,將 MyXMLString=) 錯(cuò)寫成 MyXLMString=...。
更重要的一點(diǎn)也許是,聲明的變量比未聲明的變量速度更快。由此,腳本在運(yùn)行時(shí)每次用到未聲明的變量時(shí),按名稱引用它。另一方面,聲明的變量是有順序的,要么以編譯時(shí)間,要么以運(yùn)行時(shí)間。以后,聲明的變量都按此順序引用。因?yàn)?Option Explicit 強(qiáng)制變量聲明,它能確保聲明所有變量,因此訪問的速度也很快。
技巧 11:在子例程和函數(shù)中使用局部變量
局部變量是那些在子例程和函數(shù)內(nèi)聲明的變量。在函數(shù)或子例程內(nèi),局部變量訪問比全局變量訪問更快。局部變量的使用也會(huì)使代碼更清晰,因此應(yīng)盡量使用局部變量。
技巧 12:將經(jīng)常使用的數(shù)據(jù)復(fù)制到腳本變量中
當(dāng)訪問 ASP 中的 COM 對(duì)象時(shí),應(yīng)將經(jīng)常使用的對(duì)象數(shù)據(jù)復(fù)制到腳本變量中。這樣做可減少 COM 方法調(diào)用,因?yàn)?COM 方法調(diào)用與訪問腳本變量相比,開銷相對(duì)較大。當(dāng)訪問 Collection 和 Dictionary 對(duì)象時(shí),這種技術(shù)也會(huì)減少開銷很大的查找。
一般來說,如果您打算不止一次訪問對(duì)象數(shù)據(jù),那么就應(yīng)將數(shù)據(jù)放到腳本變量中。這種優(yōu)化的主要目標(biāo)是 Request 變量(Form 和 QueryString 變量)。例如,您的站點(diǎn)可傳遞一個(gè)名為 UserID 的 QueryString 變量。假設(shè)此 UserID 在特定頁面上被引用 12 次。可以無須調(diào)用 Request(?UserID?) 12 次,而是在 ASP 頁面最上面將 UserID 指派到一個(gè)變量。然后在該頁面自始至終使用該變量。這樣就省去了 11 次 COM 方法調(diào)用。
實(shí)際上,訪問 COM 屬性或方法的開銷并沒有那么大。下面舉一個(gè)例子,說明某相當(dāng)常見的代碼(從語法上講):
Foo.bar.blah.baz = Foo.bar.blah.qaz(1)
If Foo.bar.blah.zaq = Foo.bar.blah.abc Then ' ...
當(dāng)此代碼運(yùn)行時(shí),下面是發(fā)生的情況:
變量 Foo 被解析為全局對(duì)象。 變量 bar 被解析為 Foo 的成員。這實(shí)際就是一次 COM 方法調(diào)用。 變量 blah 被解析為 Foo.bar 的成員。這又是一次 COM 方法調(diào)用。 變量 qaz 被解析為 foo.bar.blah 的成員。沒有錯(cuò),這還是一次 COM 方法調(diào)用。 調(diào)用 Foo.bar.blah.quaz(1)。再一次 COM 方法調(diào)用。懂了嗎? 再次執(zhí)行步驟 1 至步驟 3 以解析 baz。系統(tǒng)并不知道調(diào)用 qaz 是否改變對(duì)象模型,因此必須再次執(zhí)行步驟 1 至 3 以解析 baz。 將 baz 解析為 Foo.bar.blah 的成員。賦予屬性。 再次執(zhí)行步驟 1 至步驟 3 以解析 zaq。 再次執(zhí)行步驟 1 至步驟 3 以解析 abc。
正如您可看到的,效率相當(dāng)差(且慢)。以 VBScript 寫此代碼的快速方法是:
Set myobj = Foo.bar.blah ' do the resolution of blah ONCE
Myobj.baz = myobj.qaz(1)
If Myobj.zaq = Myobj.abc Then '...
如果您使用 VBScript 5.0 或更高版本,您可以使用 With 語句寫此代碼:
With Foo.bar.blah
.baz = .qaz(1)
If .zaq = .abc Then '...
...
End With
注意此技巧也適用于 VB 程序設(shè)計(jì)。
技巧 13:避免重新確定數(shù)組的維數(shù)
應(yīng)盡量避免 Redim 數(shù)組。就性能而言,如果計(jì)算機(jī)的物理內(nèi)存大小有限,最好將數(shù)組的初始維數(shù)設(shè)置為其最不利的情況 - 或?qū)⒕S數(shù)設(shè)置為其最佳的情況,然后再按需要重新確定維數(shù)。這并非意味著,如果知道您不需要內(nèi)存時(shí),就隨便分配幾兆字節(jié)的內(nèi)存。
下面的代碼給您顯示使用 Dim 和 Redim 不當(dāng)?shù)那樾巍?/p>
<%
Dim MyArray()
Redim MyArray(2)
MyArray(0) = ?hello?
MyArray(1) = ?good-bye?
MyArray(2) = ?farewell?
...
' some other code where you end up needing more space happens, then ...
Redim Preserve MyArray(5)
MyArray(3) = ?more stuff?
MyArray(4) = ?even more stuff?
MyArray(5) = ?yet more stuff?
%>
最好一開始就將數(shù)組的初始大小 Dim 正確(在本例中,是 5)比 Redim 數(shù)組使其更大好得多。您可能浪費(fèi)一些內(nèi)存(如果您沒有使用所有的元素),但獲得的好處是速度變得更快。
技巧 14:使用響應(yīng)緩沖
您可以通過啟用“響應(yīng)緩沖”,將要輸出的一整頁緩沖起來。這樣就將寫到瀏覽器的量減到最少,從而改善總體性能。每個(gè)寫操作都會(huì)產(chǎn)生很大的系統(tǒng)開銷(在 IIS 中以及在通過網(wǎng)絡(luò)發(fā)送的數(shù)據(jù)量方面),因此寫操作越少越好。由于其啟動(dòng)慢且使用 Nagling 算法(用來減輕網(wǎng)絡(luò)塞車情況),TCP/IP 在發(fā)送一些大的數(shù)據(jù)塊時(shí)比必須發(fā)送許多小的數(shù)據(jù)塊時(shí)的效率高得多。
有兩個(gè)方法啟用響應(yīng)緩沖。第一種,您可以使用 Internet Services Manager 為整個(gè)應(yīng)用程序啟用響應(yīng)緩沖。我們建議采用這種方法,在 IIS 4.0 和 IIS 5.0 中默認(rèn)為新的 ASP 應(yīng)用程序啟用響應(yīng)緩沖。第二種,可以在每個(gè) ASP 頁面的接近頂端的地方加入下面的代碼行,從而啟用響應(yīng)緩沖:
此代碼行必須在任何響應(yīng)數(shù)據(jù)被寫到瀏覽器之前執(zhí)行(即,在任何 HTML 出現(xiàn)在 ASP 腳本之前以及在使用 Response.Cookies 集合設(shè)置任何 Cookies 之前)。一般來說,最好為整個(gè)應(yīng)用程序啟用響應(yīng)緩沖。這樣,您就不必在每個(gè)頁面最上面寫入上述的代碼行。
Response.Flush
關(guān)于響應(yīng)緩沖有一個(gè)常見的抱怨,就是用戶感覺到 ASP 頁面的響應(yīng)速度很慢(即使整個(gè)響應(yīng)時(shí)間得到改進(jìn)),因?yàn)樗麄儽仨毜鹊秸麄€(gè)頁面生成,然后他們才能看到東西。對(duì)于運(yùn)行時(shí)間長的頁面,您可以設(shè)置 Response.Buffer = False,禁用響應(yīng)緩沖。但是,一個(gè)更好的策略是利用 Response.Flush 方法。這種方法將 ASP 轉(zhuǎn)換的所有 HTML 送到瀏覽器。例如,在轉(zhuǎn)換 1,000 行的表的前 100 行之后,ASP 可以調(diào)用 Response.Flush,強(qiáng)制將轉(zhuǎn)換的結(jié)果送到瀏覽器,這樣可使用戶在其余的行準(zhǔn)備好之前看到頭 100 行。這種技術(shù)可以將響應(yīng)緩沖與瀏覽器逐漸顯示數(shù)據(jù)完美地結(jié)合在一起。
(注意在上面的 1,000 行表的舉例中,許多瀏覽器在它們看到關(guān)閉標(biāo)記之前不會(huì)開始顯示表。檢查您的目標(biāo)瀏覽器是否支持。為避免這種情況,將表分成多個(gè)具有較少行的表,并在每個(gè)表之后調(diào)用 Response.Flush。較新版本的 Internet Explorer 在表完全下載之前就開始顯示表,如果您指定表列寬,顯示速度就會(huì)特別快,這樣做可避免強(qiáng)制 Internet Explorer 通過測(cè)量每個(gè)單元格的內(nèi)容寬度來計(jì)算列寬。)
另一個(gè)關(guān)于響應(yīng)緩沖的常見的抱怨是,當(dāng)產(chǎn)生非常大的頁面時(shí),將占用許多服務(wù)器內(nèi)存。撇開產(chǎn)生大頁面的方法不談,這種問題也可通過巧妙使用 Response.Flush 來加以解決。
技巧 15:批處理內(nèi)嵌腳本和 Response.Write 語句
VBScript 語法
<%
Next
While Not rs.EOF
%>
<% Next
<% rs.MoveNext
Wend %>
下面的代碼更有效,每一行對(duì)響應(yīng)流有一次寫操作。所有的代碼都包含在一個(gè) VBScript 塊內(nèi):
<%
For each fld in rs.Fields
Response.Write (?? & vbCrLf)
Next
While Not rs.EOF
Response.Write (??)
For Each fld in rs.Fields %>
Response.Write(?? & vbCrLf)
Next
Response.Write ??
Wend
%>
? & fld.Name & ? |
---|
? & fld.Value & ? |
當(dāng)禁用響應(yīng)緩沖時(shí),這一技巧的效果特別大。最好啟用響應(yīng)緩沖,然后看批處理 Response.Write 是否有助于提高性能。
(在這一特定舉例中,建立表主體的嵌套循環(huán) (While Not rs.EOF...) 可以用仔細(xì)構(gòu)建的 GetString 調(diào)用來替代。)
技巧 16:如果頁面需要很長時(shí)間才能完成,那么執(zhí)行前使用 Response.IsClientConnected
如果用戶性急,他們可能會(huì)在您開始執(zhí)行他們的請(qǐng)求之前,就會(huì)放棄 ASP 頁面。如果他們單擊刷新或移到服務(wù)器上的另一個(gè)頁面,在 ASP 請(qǐng)求隊(duì)列的末尾就有一個(gè)新的請(qǐng)求等候,在隊(duì)列的中間有一個(gè)斷開連接的請(qǐng)求。當(dāng)服務(wù)器的負(fù)載很高時(shí)(因此請(qǐng)求隊(duì)列就會(huì)很長,響應(yīng)時(shí)間也會(huì)相應(yīng)地變長),就會(huì)經(jīng)常發(fā)生這種情況,這樣只能使情況變得更糟。如果用戶不再連接,執(zhí)行 ASP 頁面(特別是慢的、大的 ASP 頁面)已沒有任何意義。您可以使用 Response.IsClientConnected 屬性檢查這一情況。如果它返回 False,則應(yīng)調(diào)用 Response.End 并放棄頁的其余部分。事實(shí)上,IIS 5.0 已將這一做法編為程序 - 每當(dāng) ASP 即將執(zhí)行新請(qǐng)求時(shí),它就會(huì)檢查請(qǐng)求在隊(duì)列中已等候了多長時(shí)間。如果已經(jīng)在那里等候了多于 3 秒鐘,ASP 將檢查客戶機(jī)是否仍處于連接狀態(tài),如果沒有連接,就立即終止請(qǐng)求。您可以在配置數(shù)據(jù)庫中使用 AspQueueConnectionTestTime 設(shè)置將超時(shí)時(shí)間由 3 秒調(diào)整為其它值。
如果頁面要花很長時(shí)間才能執(zhí)行完,也可以不時(shí)地檢查 Response.IsClientConnected。當(dāng)啟用了響應(yīng)緩沖時(shí),最好不時(shí)地執(zhí)行 Response.Flush,以用戶知道,正在發(fā)生什么事。
注意 在 IIS 4.0 上,除非先執(zhí)行了 Response.Write,否則 Response.IsClientConnected 就不能正常工作。如果啟用了緩沖,您也必須執(zhí)行 Response.Flush。在 IIS 5.0 上,卻沒有必要這樣做,- Response.IsClientConnected 工作正常。在任何情況下,Response.IsClientConnected 都會(huì)有一些開銷,因此只有在一個(gè)操作至少要花(比方說) 500 毫秒(如果您想維持每秒鐘數(shù)十頁的吞吐量,這是一個(gè)很長的時(shí)間)才使用它。經(jīng)驗(yàn)表明,不要每次重復(fù)執(zhí)行緊密循環(huán)時(shí)都調(diào)用它,如顯示表的許多行時(shí) - 每隔二十或五十行調(diào)用一次可能比較合適。