国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

通過避免下列10個常見ASP.NET缺陷使網(wǎng)站平穩(wěn)運行

2019-11-18 16:32:49
字體:
來源:轉載
供稿:網(wǎng)友

asp.net 成功的其中一個原因在于它降低了 Web 開發(fā)人員的門檻。即便您不是計算機科學博士也可以編寫 ASP.NET 代碼。我在工作中遇到的許多 ASP.NET 開發(fā)人員都是自學成材的,他們在編寫 C# 或 Visual Basic® 之前都在編寫 Microsoft® Excel® 電子表格。現(xiàn)在,他們在編寫 Web 應用程序,總的來說,他們所做的工作值得表揚。

但是與能力隨之而來的還有責任,即使是經(jīng)驗豐富的 ASP.NET 開發(fā)人員也難免會出錯。在多年的 ASP.NET 項目咨詢工作中,我發(fā)現(xiàn)某些錯誤特別容易導致缺陷不斷發(fā)生。其中某些錯誤會影響性能。其他錯誤會抑制可伸縮性。有些錯誤還會使開發(fā)團隊耗費寶貴的時間來跟蹤錯誤和意外的行為。

下面是會導致 ASP.NET 生產(chǎn)應用程序的發(fā)布過程中出現(xiàn)問題的 10 個缺陷以及可避免它們的方法。所有示例均來自我對真實的公司構建真實的 Web 應用程序的親身體驗,在某些情況下,我會通過介紹 ASP.NET 開發(fā)團隊在開發(fā)過程中遇到的一些問題來提供相關的背景。

LoadControl 和輸出緩存
極少有不使用用戶控件的 ASP.NET 應用程序。在出現(xiàn)母版頁之前,開發(fā)人員使用用戶控件來提取公用內容,如頁眉和頁腳。即使在 ASP.NET 2.0 中,用戶控件也提供了有效的方法來封裝內容和行為以及將頁面分為多個區(qū)域,這些區(qū)域的緩存能力可以獨立于作為整體的頁面進行控制(一種稱為段緩存的特殊輸出緩存形式)。

用戶控件可以采用聲明的方式加載,也可以強制加載。強制加載依賴于 Page.LoadControl,它實例化用戶控件并返回控件引用。如果用戶控件包含自定義類型的成員(例如,公共屬性),則您可以轉換該引用并從您的代碼訪問自定義成員。圖 1 中的用戶控件實現(xiàn)名為 BackColor 的屬性。以下代碼加載用戶控件并向 BackColor 分配一個值:

PRotected void Page_Load(object sender, EventArgs e){// 加載用戶控件并將其添加到頁面中Control control = LoadControl("~/MyUserControl.ascx");PlaceHolder1.Controls.Add(control);// 設置其背景色((MyUserControl)control).BackColor = Color.Yellow;}
以上代碼實際上很簡單,但卻是一個等待粗心的開發(fā)人員掉進去的陷阱。您能找出其中的破綻嗎?

如果您猜到該問題與輸出緩存有關,那么您是正確的。正如您所看到的一樣,上述代碼示例編譯和運行都正常,但是如果嘗試將以下語句(完全合法)添加到 MyUserControl.ascx 中:

<%@ OutputCache Duration="5" VaryByParam="None" %>
則當您下一次運行該頁面時,您將看到 InvalidCastException (oh joy!) 和以下錯誤消息:

“無法將類型為‘System.Web.UI.PartialCachingControl’的對象轉換為類型‘MyUserControl’。”
因此,此代碼在沒有 OutputCache 指令時運行正常,但如果添加了 OutputCache 指令就會出錯。ASP.NET 不應該以這種方式運行。頁面(和控件)對于輸出緩存應該是不可知的。那么,這代表什么意思?

問題在于為用戶控件啟用輸出緩存時,LoadControl 不再返回對控件實例的引用;相反,它返回對 PartialCachingControl 實例的引用,而 PartialCachingControl 可能會也可能不會包裝控件實例,具體取決于控件的輸出是否被緩存。因此,如果開發(fā)人員調用 LoadControl 以動態(tài)加載用戶控件并且為了訪問控件特定的方法和屬性而轉換控件引用,他們必須注意進行該操作的方式,以便不管是否具有 OutputCache 指令,代碼都可以運行。

圖 2 說明動態(tài)加載用戶控件以及轉換返回的控件引用的正確方法。以下是其工作原理概要:

• 如果 ASCX 文件缺少 OutputCache 指令,則 LoadControl 返回一個 MyUserControl 引用。Page_Load 將該引用轉換為 MyUserControl 并設置控件的 BackColor 屬性。
 
• 如果 ASCX 文件包括一個 OutputCache 指令并且控件的輸出沒有被緩存,則 LoadControl 返回一個對 PartialCachingControl 的引用,此 PartialCachingControl 的 CachedControl 屬性包含對基礎 MyUserControl 的引用。Page_Load 將 PartialCachingControl.CachedControl 轉換為 MyUserControl 并設置該控件的 BackColor 屬性。
 
• 如果 ASCX 文件包括一個 OutputCache 指令并且控件的輸出被緩存,則 LoadControl 返回一個對 PartialCachingControl(其 CachedControl 屬性為空)的引用。注意,Page_Load 不再繼續(xù)執(zhí)行操作。無法設置控件的 BackColor 屬性,因為該控件的輸出來源于輸出緩存。換句話說,根本沒有要設置屬性的 MyUserControl。
 

不管 .ascx 文件中是否具有 OutputCache 指令,圖 2中的代碼都將運行。雖然看起來復雜一點,但它會避免煩人的錯誤。簡單并不總是代表易于維護。

返回頁首
會話和輸出緩存
談到輸出緩存,ASP.NET 1.1 和 ASP.NET 2.0 都存在一個潛在的問題,該問題會影響在 Windows Server™ 2003 和 IIS 6.0 上運行的服務器中的輸出緩存頁。我曾經(jīng)親眼看到該問題在 ASP.NET 生產(chǎn)服務器中出現(xiàn)過兩次,這兩次都是通過關閉輸出緩沖來解決的。后來我了解到有一個比禁用輸出緩存更好的解決方案。以下是我第一次遇到該問題時的情況。

當時的情況是這樣的,某個網(wǎng)站(我們在此稱為 Contoso.com,它在小型 ASP.NET Web 領域中運行公共電子商務應用程序)與我的團隊聯(lián)系,抱怨他們遇到了“跨線程”錯誤。使用 Contoso.com 網(wǎng)站的客戶常常突然丟失已經(jīng)輸入的數(shù)據(jù),但卻看到另一用戶的相關數(shù)據(jù)。稍做分析即發(fā)現(xiàn),跨線程這個描述并不準確;“跨會話”錯誤更為貼切。看起來 Contoso.com 是在會話狀態(tài)中存儲數(shù)據(jù)的,由于某些原因,用戶會偶爾隨機地連接到其他用戶的會話。

我的一個團隊成員編寫了一個診斷工具,用來將每個 HTTP 請求和響應的關鍵要素(包括 Cookie 標頭)記錄到日志中。然后,他將該工具安裝在 Contoso.com 的 Web 服務器上,并讓其運行了幾天。結果非常明顯。大概每 100000 個請求中會發(fā)生一次這樣的情況:ASP.NET 正確地為全新會話分配一個會話 ID 并返回 Set-Cookie 標頭中的會話 ID。然后,它會在下一個緊相鄰的請求中返回相同的會話 ID(即,相同的 Set-Cookie 標頭),即使該請求已經(jīng)與一個有效的會話相關聯(lián)并且正確提交了 Cookie 中的會話 ID。實際上,ASP.NET 是隨機將用戶從他們自己的會話中切換出去并將他們連接到其他會話。

我們很驚訝,于是開始尋找原因。我們首先檢查了 Contoso.com 的源代碼,讓我們感到欣慰的是,問題不在那。接著,為了確保問題與應用程序宿主在 Web 領域無關,我們只保留一個服務器在運行,而關閉了所有其他服務器。問題仍然存在,這并不意外,因為我們的日志顯示匹配的 Set-Cookie 標頭絕不會來自兩個不同的服務器。ASP.NET 意外地生成了重復的會話 ID,這令人難以置信,因為它使用 .NET Framework RNGCryptoServiceProvider 類生成這些 ID,并且會話 ID 的長度足以確保相同的 ID 決不會生成兩次(至少在下一個萬億年內不會生成兩次)。除此之外,即使 RNGCryptoServiceProvider 錯誤地生成了重復的隨機數(shù)字,也無法解釋 ASP.NET 為何不可思議地將有效的會話 ID 替換為新的 ID(不唯一)。

憑直覺,我們決定看一下輸出緩存。當 OutputCacheModule 緩存 HTTP 響應時,它必須小心不要緩存了 Set-Cookie 標頭;否則,包含新會話 ID 的緩存響應會將緩存響應的所有接收者(以及其請求生成了緩存響應的用戶)連接到同一會話。我們檢查了源代碼;Contoso.com 在兩個頁面中啟用了輸出緩存。我們關閉了輸出緩存。結果,應用程序運行數(shù)天而沒有發(fā)生一個跨會話問題。此后,它運行了兩年多都沒有發(fā)生任何錯誤。在具有不同應用程序和一組不同 Web 服務器的另一家公司中,我們看到完全相同的問題也消失了。就像在 Contoso.com 一樣,消除輸出緩存就能解決問題。

Microsoft 后來確認此行為源于 OutputCacheModule 中的問題。(當您閱讀本文時,可能已經(jīng)發(fā)布了更新。)當 ASP.NET 與 IIS 6.0 一起使用并且啟用內核模式緩存時,OutputCacheModule 有時無法從它傳遞給 Http.sys 的緩存響應中刪除 Set-Cookie 標頭。下面是導致出現(xiàn)錯誤的特定事件順序:

• 最近沒有訪問網(wǎng)站(因此也沒有對應的會話)的用戶請求一個啟用了輸出緩存的頁面,但是其輸出當前在緩存中不可用。
 
• 該請求執(zhí)行用于訪問用戶最新創(chuàng)建的會話的代碼,從而導致會話 ID Cookie 在響應的 Set-Cookie 標頭中返回。
 
• OutputCacheModule 向 Http.sys 提供輸出,但是無法從響應中刪除 Set-Cookie 標頭。
 
• Http.sys 在后續(xù)的請求中返回緩存響應,誤將其他用戶連接到會話。
 

故事的寓意又是什么呢?會話狀態(tài)和內核模式輸出緩存不能混合使用。如果您在啟用輸出緩存的頁中使用會話狀態(tài),并且應用程序在 IIS 6.0 上運行,則您需要關閉內核模式輸出緩存。您仍將受益于輸出緩存,但是因為內核模式輸出緩存比普通輸出緩存快得多,所以緩存不會同樣有效。有關此問題的詳細信息,請參見 support.microsoft.com/kb/917072。

您可以通過在頁面的 OutputCache 指令中包含 VaryByParam="*" 屬性來關閉單個頁面的內核模式輸出緩存,雖然這樣做可能導致內存需求驟增。另一種更安全的方法是通過在 web.config 中包含下列元素來關閉整個應用程序的內核模式緩存:

<httpRuntime enableKernelOutputCache="false" />
您還可以使用注冊表設置來全局性地禁用內核模式輸出緩存,即禁用全部服務器的內核模式輸出緩存。有關詳細信息,請參見 support.microsoft.com/kb/820129。

每次我聽到客戶報告會話發(fā)生了費解的問題,我都會詢問他們是否在任何頁面中使用了輸出緩存。如果確實使用了輸出緩存,并且宿主操作系統(tǒng)是 Windows Server 2003,我會建議他們禁用內核模式輸出緩存。問題通常就會迎刃而解。如果問題沒有解決,則錯誤存在于代碼中。警惕!

返回頁首
Forms 身份驗證票證生存期
您能找出以下代碼的問題嗎?

FormsAuthentication.RedirectFromLoginPage(username, true);
此代碼看似沒有問題,但決不能在 ASP.NET 1.x 應用程序中使用,除非應用程序中其他位置的代碼抵消了此語句的負面作用。如果您不能確定原因,請繼續(xù)閱讀。

FormsAuthentication.RedirectFromLoginPage 執(zhí)行兩個任務。首先,當 FormsAuthenticationModule 將用戶重定向到登錄頁時,F(xiàn)ormsAuthentication.RedirectFromLoginPage 將用戶重定向到他們原來請求的頁面。其次,它發(fā)布一個身份驗證票證(通常攜帶在 Cookie 中,而且在 ASP.NET 1.x 中總是攜帶在 Cookie 中),這個票證允許用戶在預定的一段時間內保持已經(jīng)過身份驗證狀態(tài)。

問題就在于這個時間段。在 ASP.NET 1.x 中,向 RedirectFromLoginPage 傳遞另一個為 false 的參數(shù)會發(fā)出一個臨時身份驗證票證,該票證默認情況下在 30 分鐘之后到期。(您可以使用 web.config 的 元素中的 Timeout 屬性來更改超時期限。)然而,傳遞另一個為 true 的參數(shù)則會發(fā)出一個永久身份驗證票證,其有效期為 50 年!這樣就會發(fā)生問題,因為如果有人竊取了該身份驗證票證,他們就可以在票證的有效期內使用受害者的身份訪問網(wǎng)站。竊取身份驗證票證有多種方法 — 在公共無線訪問點探測未加密的通信、跨網(wǎng)站編寫腳本、以物理方式訪問受害者的計算機等等 — 因此,向 RedirectFromLoginPage 傳遞 true 比禁用您的網(wǎng)站的安全性好不了多少。幸運的是,此問題已經(jīng)在 ASP.NET 2.0 中得到了解決。現(xiàn)在的 RedirectFromLoginPage 以相同的方式接受在 web.config 中為臨時和永久身份驗證票證指定的超時。

一種解決方案是決不在 ASP.NET 1.x 應用程序的 RedirectFromLoginPage 的第二個參數(shù)中傳遞 true。但是這不切實際,因為登錄頁的特點通常是包含一個“將我保持為登錄狀態(tài)”框,用戶可以選中該框以收到永久而不是臨時身份驗證 Cookie。另一種解決方案是使用 Global.asax(如果您愿意的話,也可以使用 HTTP 模塊)中的代碼段,此代碼段會在包含永久身份驗證票證的 Cookie 返回瀏覽器之前對其進行修改。

圖 3 包含一個這樣的代碼段。如果此代碼段位于 Global.asax 中,它會修改傳出永久 Forms 身份驗證 Cookie 的 Expires 屬性,以使 Cookie 在 24 小時后過期。通過修改注釋為“新的過期日期”的行,您可以將超時設置為您喜歡的任何日期。

您可能會覺得奇怪,application_EndRequest 方法調用本地 Helper 方法 (GetCookieFromResponse) 來檢查身份驗證 Cookie 的傳出響應。Helper 方法是解決 ASP.NET 1.1 中另一個錯誤的方法,如果您使用 HttpCookieCollection 的字符串索引生成器來檢查不存在的 Cookie,此錯誤會導致虛假 Cookie 添加到響應中。使用整數(shù)索引生成器作為 GetCookieFromResponse 可以解決該問題。

返回頁首
視圖狀態(tài):無聲的性能殺手
從某種意義上說,視圖狀態(tài)是有史以來最偉大的事情。畢竟,視圖狀態(tài)使得頁面和控件能夠在回發(fā)之間保持狀態(tài)。因此,您不必像在傳統(tǒng)的 ASP 中那樣編寫代碼,以防止在單擊按鈕時文本框中的文本消失,或在回發(fā)后重新查詢數(shù)據(jù)庫和重新綁定 DataGrid。

但是視圖狀態(tài)也有缺點:當它增長得過大時,它便成為一個無聲的性能殺手。某些控件(例如文本框)會根據(jù)視圖狀態(tài)作出相應判斷。其他控件(特別是 DataGrid 和 GridView)則根據(jù)顯示的信息量確定視圖狀態(tài)。如果 GridView 顯示 200 或 300 行數(shù)據(jù),我會望而生畏。即使 ASP.NET 2.0 視圖狀態(tài)大致是 ASP.NET 1 x 視圖狀態(tài)的一半大小,一個糟糕的 GridView 也可以容易地將瀏覽器和 Web 服務器之間的連接的有效帶寬減少 50% 或更多。

您可以通過將 EnableViewState 設置為 false 來關閉單個控件的視圖狀態(tài),但某些控件(特別是 DataGrid)在不能使用視圖狀態(tài)時會失去某些功能。控制視圖狀態(tài)的更佳解決方案是將其保留在服務器上。在 ASP.NET 1.x 中,您可以重寫頁面的 LoadPageStateFromPersistenceMedium 和 SavePageStateToPersistenceMedium 方法并按您喜歡的方式處理視圖狀態(tài)。圖 4 中的代碼顯示的重寫可防止視圖狀態(tài)保留在隱藏字段中,而將其保留在會話狀態(tài)中。當與默認會話狀態(tài)進程模型一起使用時(即,會話狀態(tài)存儲在內存中的 ASP.NET 輔助進程中時),在會話狀態(tài)中存儲視圖狀態(tài)尤其有效。相反,如果會話狀態(tài)存儲在數(shù)據(jù)庫中,則只有測試才能顯示在會話狀態(tài)中保留視圖狀態(tài)會提高還是降低性能。

在 ASP.NET 2.0 中使用相同的方法,但是 ASP.NET 2.0 能夠提供更簡單的方法將視圖狀態(tài)保留在會話狀態(tài)中。首先,定義一個自定義頁適配器,其 GetStatePersister 方法返回 .NET Framework sessionPageStatePersister 類的一個實例:

public class SessionPageStateAdapter :System.Web.UI.Adapters.PageAdapter{public override PageStatePersister GetStatePersister ()    {return new SessionPageStatePersister(this.Page);    }}
然后,通過將 App.browsers 文件按以下方式放入應用程序的 App_Browsers 文件夾,將自定義頁適配器注冊為默認頁適配器:

<browsers><browser refID="Default"><controlAdapters><adapter controlType="System.Web.UI.Page"adapterType="SessionPageStateAdapter" /></controlAdapters></browser></browsers>
(您可以將文件命名為您喜歡的任何名稱,只要它的擴展名為 .browsers 即可。)此后,ASP.NET 將加載頁適配器并使用返回的 SessionPageStatePersister 以保留所有頁面狀態(tài),包括視圖狀態(tài)。

使用自定義頁適配器的一個缺點是它全局性地作用于應用程序中的每一頁。如果您更愿意將其中一些頁面的視圖狀態(tài)保留在會話狀態(tài)中而不保留其他頁面的視圖狀態(tài),請使用圖 4 中顯示的方法。另外,如果用戶在同一會話中創(chuàng)建多個瀏覽器窗口,您使用該方法可能會遇到問題。

返回頁首
SQL Server 會話狀態(tài):另一個性能殺手
ASP.NET 使得在數(shù)據(jù)庫中存儲會話狀態(tài)變得簡單:只需切換 web.config 中的開關,會話狀態(tài)就會輕松地移動到后端數(shù)據(jù)庫。對于在 Web 領域中運行的應用程序來說,這是一項重要功能,因為它允許該領域中的每個服務器共享會話狀態(tài)的一個公共庫。添加的數(shù)據(jù)庫活動降低了單個請求的性能,但是可伸縮性的提高彌補了性能的損失。

這看起來都還不錯,但是您略微考慮一下下列幾點,情況就會有所不同:

• 即使在使用會話狀態(tài)的應用程序中,大多數(shù)頁也不使用會話狀態(tài)。
 
• 默認情況下,ASP.NET 會話狀態(tài)管理器對每個請求中的會話數(shù)據(jù)存儲執(zhí)行兩個訪問(一個讀取訪問和一個寫入訪問),而不管請求的頁是否使用會話狀態(tài)。
 

換句話說,當您使用 SQL Server™ 會話狀態(tài)選項時,您在每個請求中都要付出代價(兩個數(shù)據(jù)庫訪問)— 甚至在與會話狀態(tài)無關的頁面的請求中。這會直接對整個網(wǎng)站的吞吐量造成負面影響。

 

圖 5 消除不必要的會話狀態(tài)數(shù)據(jù)庫訪問

那么您應該怎么辦呢?很簡單:禁用不使用會話狀態(tài)的頁中的會話狀態(tài)。這樣做總是一個好辦法,但是當會話狀態(tài)存儲在數(shù)據(jù)庫中時,該方法尤其重要。圖 5 顯示如何禁用會話狀態(tài)。如果頁面根本不使用會話狀態(tài),請在其 Page 指令中包含 EnableSessionState="false",如下所示:

<%@ Page EnableSessionState="false" ... %>
該指令阻止會話狀態(tài)管理器在每個請求中讀取和寫入會話狀態(tài)數(shù)據(jù)庫。如果頁面從會話狀態(tài)中讀取數(shù)據(jù),但卻不寫入數(shù)據(jù)(即,不修改用戶會話的內容),則將 EnableSessionState 設置為 ReadOnly,如下所示:

<%@ Page EnableSessionState="ReadOnly" ... %>
最后,如果頁面需要對會話狀態(tài)進行讀/寫訪問,則省略 EnableSessionState 屬性或將其設置為 true:

<%@ Page EnableSessionState="true" ... %>
通過以這種方式控制會話狀態(tài),可以確保 ASP.NET 只在真正需要時才訪問會話狀態(tài)數(shù)據(jù)庫。消除不必要的數(shù)據(jù)庫訪問是構建高性能應用程序的第一步。

順便說一下,EnableSessionState 屬性是公開的。該屬性自 ASP.NET 1.0 以來就已經(jīng)進行了說明,但是我至今仍很少見到開發(fā)人員利用該屬性。也許是因為它對于內存中的默認會話狀態(tài)模型并不十分重要。但是它對于 SQL Server 模型卻很重要。

返回頁首
未緩存的角色
以下語句經(jīng)常出現(xiàn)于 ASP.NET 2.0 應用程序的 web.config 文件以及介紹 ASP.NET 2.0 角色管理器的示例中:

<roleManager enabled="true" />
但正如以上所示,該語句確實會對性能產(chǎn)生明顯的負面影響。您知道為什么嗎?

默認情況下,ASP.NET 2.0 角色管理器不會緩存角色數(shù)據(jù)。相反,它會在每次需要確定用戶屬于哪個角色(如果有)時參考角色數(shù)據(jù)存儲。這意味著一旦用戶經(jīng)過了身份驗證,任何利用角色數(shù)據(jù)的頁(例如,使用啟用了安全裁減設置的網(wǎng)站圖的頁,以及使用 web.config 中基于角色的 URL 指令進行訪問受到限制的頁)將導致角色管理器查詢角色數(shù)據(jù)存儲。如果角色存儲在數(shù)據(jù)庫中,那么對于每個請求需要訪問多個數(shù)據(jù)庫的情況,您可以輕松地免除訪問多個數(shù)據(jù)庫。解決方案是配置角色管理器以在 Cookie 中緩存角色數(shù)據(jù):

<roleManager enabled="true" cacheRolesInCookie="true" />
您可以使用其他<roleManager> 屬性控制角色 Cookie 的特征 — 例如,Cookie 應保持有效的期限(以及角色管理器因此返回角色數(shù)據(jù)庫的頻率)。角色 Cookie 默認情況下是經(jīng)過簽名和加密的,因此安全風險雖然不為零,但也有所緩解。

返回頁首
配置文件屬性序列化
ASP.NET 2.0 配置文件服務為保持每個用戶的狀態(tài)(例如個性化首選項和語言首選項)的問題提供了一個現(xiàn)成的解決方案。要使用配置文件服務,您可以定義一個 xml 配置文件,其中包含要保留的代表單個用戶的屬性。然后,ASP.NET 編譯一個包含相同屬性的類,并通過添加到頁的配置文件屬性提供對類實例的強類型訪問。

配置文件靈活性很強,它甚至允許將自定義數(shù)據(jù)類型用作配置文件屬性。但是,其中卻存在一個問題,我親眼看到該問題導致開發(fā)人員出差錯。圖 6 包含一個名為 Posts 的簡單類,以及將 Posts 用作配置文件屬性的配置文件定義。但是,該類和該配置文件在運行時會產(chǎn)生意外的行為。您能找出其中的原因嗎?

問題在于 Posts 包含一個名為 _count 的私有字段,該字段必須進行序列化和反序列化,才能完全凍結和重新凍結類實例。但是 _count 卻沒有經(jīng)過序列化和反序列化,因為它是私有的,而且默認情況下 ASP.NET 配置文件管理器使用 XML 序列化對自定義類型進行序列化和反序列化。XML 序列化程序將忽略非公共成員。因此,會對 Posts 的實例進行序列化和反序列化,但是每次反序列化類實例時,_count 都會重設為 0。

一種解決方案是使 _count 成為公共字段而非私有字段。另一種解決方案是使用公共讀/寫屬性封裝 _count。最佳解決方案是將 Posts 標記為可序列化(使用 SerializableAttribute),并將配置文件管理器配置為使用 .NET Framework 二進制序列化程序對類實例進行序列化和反序列化。該解決方案能夠保持類本身的設計。與 XML 序列化程序不同的是,二進制序列化程序序列化字段,而不管是否可以訪問。圖 7 顯示 Posts 類的修復版本并突出顯示了更改的附帶配置文件定義。

您應該牢記的一點是,如果您使用自定義數(shù)據(jù)類型作為配置文件屬性,并且該數(shù)據(jù)類型具有必須序列化才能完全序列化類型實例的非公共數(shù)據(jù)成員,則在屬性聲明中使用 serializeAs="Binary" 屬性并確保類型本身是可序列化的。否則,將無法進行完整的序列化,并且您還將浪費時間來嘗試確定配置文件無法工作的原因。

返回頁首
線程池飽和
在執(zhí)行數(shù)據(jù)庫查詢并等待 15 秒或更長時間來獲得返回的查詢結果時,我經(jīng)常對看到的實際的 ASP.NET 頁數(shù)感到非常驚訝。(我也等待了 15 分鐘才看到查詢結果!)有時,延遲是由于返回的數(shù)據(jù)量很大而導致的不可避免的無奈結果;而有時,延遲則是由于數(shù)據(jù)庫的設計不佳導致的。但不管是什么原因,長時間的數(shù)據(jù)庫查詢或任何類型的長時間 I/O 操作在 ASP.NET 應用程序中都會導致吞吐量的下降。

關于這個問題我以前已經(jīng)詳細地描述過,所以在此就不再作過多的說明了。我只說一點就夠了,ASP.NET 依賴于有限的線程池處理請求,如果所有線程都被占用來等待數(shù)據(jù)庫查詢、Web 服務調用或其他 I/O 操作完成,則在某個操作完成并且釋放出一個線程之前,其他請求都必須排隊等待。當請求排隊時,性能會急劇下降。如果隊列已滿,則 ASP.NET 會使隨后的請求失敗并出現(xiàn) HTTP 503 錯誤。這種情況不是我們希望在 Web 生產(chǎn)服務器的生產(chǎn)應用程序上所樂見的。

解決方案非異步頁面莫屬,這是 ASP.NET 2.0 中最佳卻鮮為人知的功能之一。對異步頁面的請求從一個線程上開始,但是當它開始一個 I/O 操作時,它將返回該線程以及 ASP.NET 的 IAsyncResult 接口。操作完成后,請求通過 IAsyncResult 通知 ASP.NET,ASP.NET 從池中提取另一個線程并完成對請求的處理。值得注意的是,當 I/O 操作發(fā)生時,沒有占用線程池線程。這樣可以通過阻止其他頁面(不執(zhí)行較長的 I/O 操作的頁面)的請求在隊列中等待,從而顯著地提高吞吐量。

您可以在 MSDN®Magazine 的 2005 年 10 月刊中閱讀有關異步頁面的所有信息。I/O 綁定而不是計算機綁定且需要很長時間執(zhí)行的任何頁面很有可能成為異步頁面。

當我將關于異步頁面的信息告知開發(fā)人員時,他們經(jīng)常回答“那真是太棒了,但是我的應用程序中并不需要它們。”對此我回答說:“你們的任何頁面需要查詢數(shù)據(jù)庫嗎?它們調用 Web 服務嗎?您是否已經(jīng)檢查 ASP.NET 性能計數(shù)器中關于排隊請求和平均等待時間的統(tǒng)計信息?即使您的應用程序至今運行正常,但是隨著您的客戶規(guī)模的增長,應用程序的負載可能會增加。”

實際上,絕大多數(shù)實際的 ASP.NET 應用程序都需要異步頁面。請切記這一點!

返回頁首
模擬和 ACL 授權
以下是一個簡單的配置指令,但是每當在 web.config 中看到它時都讓我眼前一亮:

<identity impersonate="true" />
此指令在 ASP.NET 應用程序中啟用客戶端模擬。它將代表客戶端的訪問令牌附加到處理請求的線程,以便操作系統(tǒng)執(zhí)行的安全性檢查針對的是客戶端身份而不是輔助進程身份。ASP.NET 應用程序很少需要模擬;我的經(jīng)驗告訴我,開發(fā)人員通常都是由于錯誤的原因而啟用模擬的。以下是原因所在。

開發(fā)人員經(jīng)常在 ASP.NET 應用程序中啟用模擬,以便可以使用文件系統(tǒng)權限來限制對頁面的訪問。如果 Bob 沒有查看 Salaries.aspx 的權限,則開發(fā)人員將會啟用模擬,以便可以通過將訪問控制列表 (ACL) 設置為拒絕 Bob 的讀取權限,阻止 Bob 查看 Salaries.aspx。但是存在以下隱患:對于 ACL 授權來說,模擬是不必要的。在 ASP.NET 應用程序中啟用 Windows 身份驗證時,ASP.NET 會自動為請求的每個 .aspx 頁面檢查 ACL 并拒絕沒有讀取文件權限的調用者的請求。即使禁用了模擬,它仍會這樣操作。

有的時候需要證明模擬的合理性。但是您通常可以用良好的設計來避免它。例如,假定 Salaries.aspx 在數(shù)據(jù)庫中查詢只有管理人員才能知道的工資信息。通過模擬,您可以使用數(shù)據(jù)庫權限拒絕非管理人員查詢工資數(shù)據(jù)的能力。或者您可以不考慮模擬,并且通過為 Salaries.aspx 設置 ACL 以使非管理人員不具有讀取權限,從而限制對工資數(shù)據(jù)的訪問。后一種方法提供的性能更佳,因為它完全避免了模擬。它也消除了不必要的數(shù)據(jù)庫訪問。為什么查詢數(shù)據(jù)庫僅由于安全原因被拒絕?

順便說一下,我曾經(jīng)幫助對一個傳統(tǒng)的 ASP 應用程序進行故障排除,該應用程序由于內存占用不受限制而定期重新啟動。一個沒有經(jīng)驗的開發(fā)人員將目標 SELECT 語句轉換成了 SELECT *,而沒有考慮要查詢的表包含圖像,這些圖像很大而且數(shù)目很多。問題由于未檢測到內存泄漏而惡化。(我的托管代碼領域!)多年來運行正常的應用程序開始突然停止工作,因為以前返回一兩千字節(jié)數(shù)據(jù)的 SELECT 語句現(xiàn)在卻返回了幾兆字節(jié)。如果再加上不充分的版本控制,開發(fā)團隊的生活將不得不“亢奮起來”— 這里所謂的“亢奮”,就如同當您在晚上要睡覺時,還不得不看著您的孩子玩令人厭煩的足球游戲一樣。

理論上,傳統(tǒng)的內存泄漏不會發(fā)生在完全由托管代碼組成的 ASP.NET 應用程序中。但是內存使用量不足會通過強制垃圾收集更頻繁地發(fā)生而影響性能。即使是在 ASP.NET 應用程序中,也要警惕 SELECT *!

返回頁首
不要完全信賴它 — 請設置數(shù)據(jù)庫的配置文件!
作為一名顧問,我經(jīng)常被詢問為何應用程序沒有按預期執(zhí)行。最近,有人詢問我的團隊為何 ASP.NET 應用程序只完成請求文檔所需吞吐量(每秒的請求數(shù))的大約 1/100。我們以前所發(fā)現(xiàn)的問題是我們在不能正常運行的 Web 應用程序中發(fā)現(xiàn)的問題特有的 — 和我們所有人應該認真對待的教訓。

我們運行 SQL Server Profiler 并監(jiān)視此應用程序和后端的數(shù)據(jù)庫之間的交互情況。在一個更極端的案例中,僅僅只是一個按鈕單擊,就導致數(shù)據(jù)庫發(fā)生了 1,500 多個錯誤。您不能那樣構建高性能的應用程序。良好的體系結構總是從良好的數(shù)據(jù)庫設計開始。不管您的代碼的效率有多高,如果它被編寫不佳的數(shù)據(jù)庫所拖累,就會不起作用。

糟糕的數(shù)據(jù)訪問體系結構通常源于下面的一個或多個方面:

• 拙劣的數(shù)據(jù)庫設計(通常由開發(fā)人員設計,而不是數(shù)據(jù)庫管理員)。
 
• DataSets 和 DataAdapters 的使用 — 尤其是 DataAdapter.Update,它適用于 Windows 窗體應用程序和其他胖客戶端,但是對于 Web 應用程序來說通常不理想。
 
• 具有拙劣編制計算程序、以及執(zhí)行相對簡單的操作需消耗很多 CPU 周期的設計糟糕的數(shù)據(jù)訪問層 (DAL)。
 

必須先確定問題才能對其進行處理。確定數(shù)據(jù)訪問問題的方式是運行 SQL Server Profiler 或等效的工具以查看后臺正在執(zhí)行的操作。檢查應用程序和數(shù)據(jù)庫之間的通信之后,性能調整才完成。嘗試一下 — 您可能會對您的發(fā)現(xiàn)大吃一驚。

返回頁首
結論
現(xiàn)在您已經(jīng)了解在生成 ASP.NET 生產(chǎn)應用程序過程中可能遇到的一些問題及其解決方案了。下一步是仔細查看您自己的代碼并嘗試避免我在此概述的一些問題。ASP.NET 可能降低了 Web 開發(fā)人員的門檻,但是您的應用程序完全有理由靈活、穩(wěn)定和高效。請認真考慮,避免出現(xiàn)新手易犯的錯誤。

圖 8 提供了一個簡短檢查列表,您可以使用它來避免本文中描述的缺陷。您可以創(chuàng)建一個類似的安全缺陷檢查列表。例如:

• 您是否已經(jīng)對包含敏感數(shù)據(jù)的配置節(jié)進行加密?
 
• 您是否正在檢查并驗證在數(shù)據(jù)庫操作中使用的輸入,是否使用了 HTML編碼輸入作為輸出?
 
• 您的虛擬目錄中是否包含具有不受保護的擴展名的文件?
 

如果您重視網(wǎng)站、承載網(wǎng)站的服務器以及它們所依賴的后端資源的完整性,則這些問題非常重要。

Jeff Prosise 是對 MSDN Magazine 貢獻很大的編輯以及多本書籍的作者,這些書籍中包括 Programming Microsoft .NET (Microsoft Press, 2002)。他也是軟件咨詢和教育公司 Wintellect 的共同創(chuàng)始人。

摘自 MSDN Magazine 的 2006 年 7 月刊。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 南昌市| 百色市| 炉霍县| 阳原县| 泸定县| 齐河县| 孟州市| 杭锦旗| 长阳| 嫩江县| 资源县| 怀化市| 英德市| 高唐县| 公安县| 和平县| 南充市| 江都市| 淮南市| 青铜峡市| 牟定县| 涟源市| 博野县| 卢湾区| 田林县| 高青县| 葫芦岛市| 房产| 松滋市| 吉林省| 安庆市| 江都市| 利津县| 阿勒泰市| 北辰区| 安新县| 巴林右旗| 怀化市| 永平县| 土默特右旗| 会东县|