技巧 4:避免將非敏捷的組件緩衝在 Application 或 Session 對象中
儘管將資料緩衝在 Application 或 Session 對象中是一個好的做法,但緩衝 COM 對象卻有嚴重的陷阱。通常,人們傾向於將經常使用的 COM 物件緩衝到 Application 或 Session 對象中。很遺憾,許多 COM 物件(包括所有以 Visual Basic 6.0 或更低版本編寫的對象)當儲存在 Application 或 Session 對象時,會引起嚴重的瓶頸。
具體來講,當任何不敏捷的組件緩衝在 Session 或 Application 對象時,將引起效能瓶頸。敏捷的組件是被標記為 ThreadingModel=Both 的組件,它聚集 Free-threaded marshaler (FTM);或被標記為 ThreadingModel=Neutral 的組件。(Neutral 模型是 Windows 2000 和 COM+ 的新增模型。) 下列組件不是敏捷的:自由線程的組件(除非它們聚集 FTM)。
Apartment 執行緒元件。
單執行緒元件。
配置的組件(Microsoft Transaction Server (MTS)/COM+ 庫和伺服器程式包/應用程式)不是敏捷的,除非它們是 Neutral 線程。Apartment 執行緒元件和其它非敏捷的組件在頁範圍內是最適合的(即,它們在單個 ASP 頁上建立和銷毀)。
在 IIS 4.0 中,被標記為 ThreadingModel=Both 的組件被認為是敏捷的。在 IIS 5.0 中,只有這一點還不夠。組件必須不僅被標記 Both,還必須聚集 FTM。有關敏捷性的文章講述了如何使以 Active Template Library 編寫的 C++ 組件聚集 FTM。要注意如果組件緩衝介面指標,那麼那些指標本身必須是敏捷的,或必須儲存在 COM 共用介面表 (GIT) 中。如果您不能重新編譯 Both 執行緒元件以聚集 FTM,那麼您可以將組件標記為 ThreadingModel=Neutral。或者,如果您不想讓 IIS 執行敏捷性檢查(因此,您可以允許非敏捷的組件儲存在 Application 或 Session 範圍中),您可以在設定資料庫中將 AspTrackThreadingModel 設定為 True。不建議更改 AspTrackThreadingModel。
如果您想將以 Server.CreateObject 建立的非敏捷的組件儲存在 Application 對象中,IIS 5.0 將出現一個錯誤。您可以在 Global.asa 中使用 <object runat=server scope=application ...> 避免這一錯誤,但不建議這樣做,因為這會導致彙集和序列化,關於這一點將在下面講述。
如果您緩衝非敏捷的組件會出現什麼毛病?緩衝在 Session 對象中的非敏捷的組件將 Session 鎖定於 ASP 工作者線程。ASP 維護一個工作者線程池來處理請求。通常情況下,一個新請求總是由第一個可用的工作者線程來處理。如果 Session 被鎖定於一個線程,那麼請求必須等到其相關的線程可用為止。這裡有一個類比,也許會有所協助:您去一家超級市場,挑選了一些商品,並在 #_3 收款台付款。其後,每當您在那家超級市場為商品付款時,您總是必須在 #_3 收款台付款,即使其它收款台前排隊的人較少或者沒有人排隊,也是如此。
將非敏捷的組件儲存在 Application 範圍對效能的影響甚至更壞。ASP 必須建立一個特殊的線程運行儲存在 Application 範圍中的非敏捷組件。這會有兩個結果:所有調用都必須彙集到此線程,且所有調用都排成長隊。“彙集”的意思是參數必須儲存在記憶體的共用地區;執行一個開銷很大的到特殊線程的環境切換;動作項目的方法;將結果彙集到共用地區;執行另一個開銷很大的環境切換,將控制返回到原始的線程。“序列化”意思是指每次只運行一個方法。兩個不同的 ASP 工作者線程不能同時在共用組件上執行多個方法。這樣就杜絕了並發性,特別是在多處理器電腦上。更糟的是,所有非敏捷的 Application 範圍的組件共用一個線程(主機 STA),因此序列化的影響甚至更顯著。
如之奈何?下面是一些一般的規則。如果您使用 Visual Basic (6.0) 或更早版本編寫對象,那麼不要將它們緩衝在 Application 或 Session 對象中。如果您不知道對象的執行緒模式,不要緩衝它。不要緩衝非敏捷的對象,而應在每個頁面建立和釋放它們。對象直接在 ASP 工作者線程上運行,因此沒有彙集或序列化。如果 COM 物件在 IIS 伺服器上運行,且如果它們不花長時間初始化和刪除,效能尚可。注意單線程對象不應該這樣使用。小心 - VB 可建立單線程對象!如果您必須這樣使用單線程對象(如 Microsoft Excel 試算表),別指望會有很高的輸送量。
當 ADO 被標記為自由線程,ADO 記錄集可以安全地緩衝。要將 ADO 標記為自由線程,使用 Makfre15.bat 檔案,該檔案通常位於目錄 //Program Files/Common/System/ADO 中。
警告 如果您使用 Microsoft Access 作為資料庫,不應將 ADO 標記為自由線程的。ADO 記錄集也必須切斷串連。一般來說,如果您不能控制網站中的 ADO 配置(例如,您是一個獨立的軟體廠商 [ISV],向管理他們自己的配置客戶銷售 Web 應用程式),最好不要緩衝記錄集。
詞典組件也是敏捷的對象。LookupTable 從資料檔案中裝載其資料,可用於組合框資料和配置資訊。Duwamish Books 中的 PageCache 對象可提供詞典文法,Caprock Dictionary 也可提供。這些對象或其派生對象可以構成有效緩衝策略的基礎。注意 Scripting.Dictionary 對象不是敏捷的,不應該儲存在 Application 或 Session 範圍中。
技巧 5:不要將資料庫連接緩衝在 Application 或 Session 對象中
緩衝 ADO 連線通常是很糟糕的策略。如果一個 Connection Object Storage Service在 Application 對象中,並在所有的頁面中使用,那麼所有頁面將爭搶這一串連。如果 Connection Object Storage Service在 ASP Session 對象中,那麼將為每個使用者建立資料庫連接。這就會使串連池的優勢蕩然無存,並給 Web 伺服器和資料庫帶來不必要的壓力。
可以不快取資料庫串連,而是在使用 ADO 的每個 ASP 頁面中建立和刪除 ADO 對象。這是很有效,因為 IIS 內嵌了資料庫連接池。更準確地說,IIS 自動啟用 OLEDB 和 ODBC 串連池。這就能確保在每個頁面上建立和刪除串連將是有效。
因為串連的記錄集儲存一個到資料庫連接的引用,所以您不應將串連的記錄集緩衝在 Application 或 Session 對象中。但是,您可以安全地緩衝中斷連線的記錄集,它們不儲存到其資料連線的引用。要斷開記錄集串連,執行下面的兩個步驟:
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
有關串連池的更詳細資料,可以在 ADO 和 SQL Server 參考資料中找到。
技巧 6:合理地使用 Session 對象
既然我們已經討論了緩衝在 Application 和 Session 中的優點,現在開始討論避免使用 Session 對象的問題。正如下面所討論的,當與忙的網站一起使用時,Session 有幾個缺點。“忙”的意思一般是指一秒鐘要求幾百頁面或成千上萬同時使用者的網站。這個技巧對於必須水平擴充的網站 - 即,那些利用多台伺服器以處理負載或實現容錯的網站 - 甚至更重要。對於較小的網站,諸如 Intranet 網站,要想實現 Session 帶來的方,必然增大系統開銷。
簡言之,ASP 自動為每個訪問 Web 服務器的使用者建立一個 Session。每個 Session 大約需要 10 KB 的記憶體開銷(最主要的是資料存放區在 Session 中),這就使所有的請求都減慢。在配置的逾時時段(通常是 20 分鐘)結束以前,Session 一直保留有效。
Session 的最大的問題不是效能,而是可擴充性。Session 不能跨越幾台 Web 服務器,一旦在一台伺服器上建立 Session,其資料就留在那兒。這就意味著如果您在一個 Web 服務器群使用 Session,您必須設計一個策略,將每個使用者請求始終發到使用者 Session 所在的那台伺服器上。這被稱為將使用者“粘”在 Web 服務器上。術語“粘性會話”就是從這裡派生而來的。如果 Web 伺服器崩潰,被“粘住的”使用者將丟失他們的工作階段狀態,因為會話不是粘到磁碟上。
實現粘性會話的策略包括硬體和軟體解決方案。諸如 Windows 2000 Advanced Server 中的網路Server Load Balancer和 Cisco 的 Local Director 之類的解決方案都可以實現粘性會話,代價是要損失一定程度的可擴充性。這些解決方案是不完善的。不建議此時部署您自己的軟體解決方案(我們過去常常使用 ISAPI 篩選器和 URL 轉換等等)。
Application 對象也不跨越多台伺服器,如果您必須跨越 Web 服務器群共用和更新 Application 資料,您必須使用後端資料庫。但是,唯讀 Application 資料在 Web 伺服器群中仍是有用的。
如果只是因為要增加已耗用時間(處理容錯移轉和伺服器維護),大多數關鍵任務網站至少需部署兩台 Web 伺服器。因此,在設計關鍵任務應用程式時,必須實現“粘性會話”,或乾脆避免使用 Session,以及任何其它將使用者狀態儲存在單個 Web 伺服器上的狀態管理技術。
如果您不使用 Session,一定要將它們關閉。您可以通過 Internet Services Manager,為應用程式執行此操作(參見 ISM 文檔)。如果您決定使用 Session,您可以採用一些方法減輕它們對效能的影響。
您可以將不需要 Session 的內容(如協助螢幕,訪問者地區等等)移到另一個關閉了 Session 的 ASP 應用程式中。您可以逐頁提示 ASP,您不再需要該頁面上的 Session 對象,使用以下放在 ASP 頁面最上面的指令:
<% @EnableSessionState=False %>
使用這一指令有一個很好的理由是,這些 Session 在框架組方面存在一個有意思的問題。ASP 保證任何時候 Session 只有一個請求執行。這樣就確保如果瀏覽器為一個使用者請求多個頁面,一次只有一個 ASP 請求接觸 Session,這樣就避免了當訪問 Session 對象時發生的多線程問題。很遺憾,一個框架組中的所有頁面將以串列方式顯示,一個接一個,而不是同時顯示。使用者可能必須等候很長時間,才能看到所有的架構。該故事的寓意:如果某些框架組頁面不依靠 Session,一定要使用 @EnableSessionState=False 指令告訴 ASP。
有許多管理 Session 狀態的方法,可替代 Session 對象的使用。對於少量的狀態(少於 4 KB),我們通常建議使用 Cookies、QueryString 變數和隱式變數。對於更大資料量,如購物小車,後端資料庫是最適合的選擇。有關 Web 服務器群中狀態管理技術的文章很多。有關詳細資料,請參見 Session 狀態參考資料。
技巧 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 NT 4.0/IIS 4.0 相比,Windows 2000/IIS 5.0 中在指令碼和 ADO 效能方面有了很大的改進。因此,隨著 IIS 5.0 的推出,編譯代碼比 ASP 代碼的效能優勢有所降低。
有關在 ASP 中使用 COM 的優點和缺點的詳細討論,參見 ASP Component Guidelines and Programming Distributed Applications with and Microsoft Visual Basic 6.0。如果您部署 COM 組件,以負荷對它們進行測試特別重要。事實上,理所當然應對所有的 ASP 應用程式進行負荷測試。
技巧 8:遲一點獲得資源,早一點釋放資源
這裡是一個小技巧供您參考。一般來說,最好遲一點獲得資源,早一點釋放資源。這適用於 COM 對象以及檔案控制代碼和其它資源。
這種最佳化方法主要用於 ADO 串連和記錄集。當您使用完記錄集,比方說在顯示一個表及其資料之後,應立即釋放它,而不是等到頁面結束時再釋放。將 VBScript 變數設定為 Nothing 是最好的做法。不要讓記錄集超出範圍之外。而且,要釋放任何相關的 Command 或 Connection 對象(在將記錄集或串連設定為 = Nothing 之前,不要忘記調用 Close())。這會縮短資料庫必須為您準備資源的時間,並儘快釋放資料庫到串連池的串連。
技巧 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 選項
COM 組件也有三種配置選項,雖然與 ASP 選項不完全類似。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 伺服器可以以低隔離級運行。衡量各種利弊,並自己決定哪個配置更能符合您的需要。