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

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

編寫(xiě)高性能Web應(yīng)用程序的10個(gè)技巧

2019-11-18 17:16:28
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

作者:Rob Howard    譯:寒帶魚(yú)

 

這篇文章討論了:

·一般asp.net性能的秘密

·能提高ASP.NET表現(xiàn)的有用的技巧和竅門(mén)

·在ASP.NET中使用數(shù)據(jù)庫(kù)的建議

·ASP.NET中的緩存和后臺(tái)處理

 

       使用ASP.NET編寫(xiě)一個(gè)Web應(yīng)用程序是難以置信的簡(jiǎn)單的。太簡(jiǎn)單了,以至于很多開(kāi)發(fā)者都不花費(fèi)時(shí)間來(lái)構(gòu)建他們的應(yīng)用程序來(lái)達(dá)到很好的表現(xiàn)。在這篇文章里,我將為編寫(xiě)高性能的Web應(yīng)用程序推薦10個(gè)技巧。我不會(huì)講我的論述局限于ASP.NET應(yīng)用程序,因?yàn)锳SP.NET應(yīng)用程序只是Web應(yīng)用程序的一個(gè)子集而已。這篇文章不會(huì)是針對(duì)優(yōu)化Web應(yīng)用程序的性能的權(quán)威性指導(dǎo)——一本完整的書(shū)可以很容易的做到這一點(diǎn)。相反,我們應(yīng)該把這篇文章當(dāng)成一個(gè)好的起點(diǎn)。

 

       在成為一個(gè)工作狂以前,我會(huì)經(jīng)常去攀巖。在做任何攀巖活動(dòng)之前,我更愿意看看旅行指南里面的路線,再讀讀那些曾經(jīng)到過(guò)峰頂?shù)娜俗龅耐扑]。但是,不管旅行指南寫(xiě)的有多好,在嘗試一個(gè)有挑戰(zhàn)性的目標(biāo)之前,您都需要有實(shí)際的攀巖經(jīng)驗(yàn)。與之相似,當(dāng)您面臨修復(fù)性能問(wèn)題或者運(yùn)行一個(gè)高吞吐量站點(diǎn)的問(wèn)題時(shí),您只能學(xué)習(xí)如何編寫(xiě)高性能 Web 應(yīng)用程序。

 

 

       我的個(gè)人經(jīng)驗(yàn)來(lái)自在微軟的ASP.NET團(tuán)隊(duì)中擔(dān)任過(guò)一名基礎(chǔ)程序經(jīng)理的經(jīng)歷,維護(hù)和管理www.asp.net,還有幫助構(gòu)架Community Server,它是幾個(gè)著名的ASP.NET應(yīng)用程序(ASP.NET Forums,.Text,和連接到一個(gè)平臺(tái)的nGallery)的下一個(gè)版本。我相信這些曾經(jīng)幫助過(guò)我技巧中的一些也會(huì)對(duì)您有用的。

      

       您應(yīng)該考慮把您的應(yīng)用程序分離為幾個(gè)邏輯層次。您可能已經(jīng)聽(tīng)說(shuō)過(guò)3層(或者n層)體系結(jié)構(gòu)。這些通常都是規(guī)定的結(jié)構(gòu)模式,它們將業(yè)務(wù)和(或)硬件從物理上進(jìn)行了功能劃分。如果系統(tǒng)需要更大的規(guī)模,更多的硬件可以輕松的加進(jìn)來(lái)。然而,那會(huì)產(chǎn)生一個(gè)與業(yè)務(wù)和機(jī)器跳躍相關(guān)聯(lián)的性能下降,因此我們應(yīng)該避免它。所以只要可能,盡量在同一個(gè)應(yīng)用程序中運(yùn)行ASP.NET頁(yè)面和頁(yè)面的相關(guān)組件

 

       因?yàn)榇a的分離和層次之間的邊界,使用Web服務(wù)或者遠(yuǎn)程處理會(huì)降低性能20%甚至更多。

 

       數(shù)據(jù)層有點(diǎn)與眾不同,因?yàn)橥ǔG闆r下,最好具有專(zhuān)用于數(shù)據(jù)庫(kù)的硬件。然而,然而進(jìn)程跳躍到數(shù)據(jù)庫(kù)的成本依然很高,因此在數(shù)據(jù)層的性能是您優(yōu)化代碼時(shí)應(yīng)該首先考慮的。

 

       在投入到修復(fù)您的應(yīng)用程序的性能問(wèn)題之前,確保您要先分析您的應(yīng)用程序來(lái)發(fā)現(xiàn)問(wèn)題的根源所在。關(guān)鍵性能計(jì)數(shù)器(例如那個(gè)指示在執(zhí)行垃圾收集過(guò)程中花費(fèi)的時(shí)間百分比的計(jì)數(shù)器)在找出應(yīng)用程序在哪里花費(fèi)了主要的時(shí)間時(shí)也是非常有用的。雖然那些花費(fèi)時(shí)間的地方經(jīng)常是不那么直觀的。

 

       在這篇文章中我討論了兩種改進(jìn)性能的方法:大塊的優(yōu)化,例如使用ASP.NET緩存,還有小塊的優(yōu)化,它們經(jīng)常重復(fù)出現(xiàn)。這些小塊的優(yōu)化有時(shí)是最有意思的。您對(duì)代碼的一個(gè)小的修改會(huì)被調(diào)用成千上萬(wàn)次。對(duì)大塊的優(yōu)化,您可能會(huì)發(fā)現(xiàn)整個(gè)的性能有了一個(gè)大的飛躍。對(duì)小塊的優(yōu)化,您可能會(huì)縮減了對(duì)一個(gè)給定請(qǐng)求的幾微秒的時(shí)間,但是如果把每天的所有的請(qǐng)求累積起來(lái),性能就會(huì)得到一個(gè)意想不到的改進(jìn)。

 

數(shù)據(jù)層中的性能
 

       當(dāng)您要開(kāi)始優(yōu)化一個(gè)應(yīng)用程序的性能的時(shí)候,有一個(gè)決定性的測(cè)試您可以?xún)?yōu)先考慮使用:代碼是否要訪問(wèn)數(shù)據(jù)庫(kù)?如果是,多長(zhǎng)時(shí)間訪問(wèn)一次?注意這個(gè)測(cè)試也可以應(yīng)用到那些使用Web服務(wù)或者遠(yuǎn)程控制的代碼中,但是我不會(huì)在這篇文章中涉及那些內(nèi)容。

 

       如果在您的代碼中的某個(gè)代碼路徑中要求一個(gè)數(shù)據(jù)庫(kù)請(qǐng)求,而您發(fā)現(xiàn)其他地方您想要優(yōu)先優(yōu)化,例如字符串操作,那么停下來(lái)然后先執(zhí)行關(guān)鍵性的測(cè)試。除非您有一個(gè)性能實(shí)在糟糕的問(wèn)題要處理,否則您的時(shí)間會(huì)得到更好的利用,如果您把時(shí)間花在優(yōu)化數(shù)據(jù)庫(kù)連接的時(shí)間,返回的數(shù)據(jù)量,還有您作的往返數(shù)據(jù)庫(kù)的操作中。

 

       現(xiàn)在我已經(jīng)總體介紹了相關(guān)的信息,下面讓我們看看10條幫您的應(yīng)用程序表現(xiàn)更好的技巧。我會(huì)從那些對(duì)改善性能效果最明顯的地方開(kāi)始說(shuō)。

 

技巧 1——返回多個(gè)結(jié)果集
 

       查看一下您的數(shù)據(jù)庫(kù)代碼,看看您是否有訪問(wèn)數(shù)據(jù)庫(kù)多于一次的請(qǐng)求路徑(request paths)。每個(gè)這樣的往返都回降低您的應(yīng)用程序每秒可以服務(wù)的請(qǐng)求的數(shù)量。通過(guò)在一次數(shù)據(jù)庫(kù)請(qǐng)求中返回多個(gè)結(jié)果集,您可以減少數(shù)據(jù)庫(kù)通信消耗的總時(shí)間。在您減少了數(shù)據(jù)庫(kù)服務(wù)器管理的請(qǐng)求之后,您也會(huì)使您的系統(tǒng)更具可升級(jí)性。

 

       一般您可以使用動(dòng)態(tài)SQL語(yǔ)句來(lái)返回多個(gè)結(jié)果集,我更喜歡用存儲(chǔ)過(guò)程。是否應(yīng)該把業(yè)務(wù)邏輯放在存儲(chǔ)過(guò)程中是存在爭(zhēng)議的,但我認(rèn)為如果一個(gè)存儲(chǔ)過(guò)程中的邏輯可以限制返回的數(shù)據(jù)(減少數(shù)據(jù)集的大小,花在網(wǎng)絡(luò)連接上的時(shí)間,并且不需要過(guò)濾邏輯層的數(shù)據(jù)),那它就是好東西。

 

       使用一個(gè)SqlCommand實(shí)例和它的ExecuteReader方法來(lái)生成強(qiáng)類(lèi)型的業(yè)務(wù)類(lèi),您可以通過(guò)調(diào)用NextResult讓結(jié)果集指針向前移動(dòng)。圖1展示了一個(gè)使用定義的類(lèi)生成幾個(gè)ArrayList的示例會(huì)話。只從數(shù)據(jù)庫(kù)返回您需要的數(shù)據(jù)會(huì)顯著地減少您服務(wù)器上的內(nèi)存申請(qǐng)。


 

 1// read the first resultset
 2reader = command.ExecuteReader();
 3
 4// read the data from that resultset
 5while (reader.Read()) {
 6    suppliers.Add(PopulateSupplierFromIDataReader( reader ));
 7}
 8
 9// read the next resultset
10reader.NextResult();
11
12// read the data from that second resultset
13while (reader.Read()) {
14    PRoducts.Add(PopulateProductFromIDataReader( reader ));
15}
16
17

 

技巧 2——分頁(yè)數(shù)據(jù)訪問(wèn)
 

       ASP.NET的DataGrid提供了一個(gè)非常棒的能力:對(duì)數(shù)據(jù)分頁(yè)的支持。當(dāng)在DataGrid中設(shè)置了分頁(yè),那么將一次顯示一個(gè)特定數(shù)目的結(jié)果。此外,用來(lái)在結(jié)果之間導(dǎo)航的分頁(yè)UI也會(huì)在DataGrid的底部顯示出來(lái)。分頁(yè)UI允許您在顯示的數(shù)據(jù)之間向前導(dǎo)航或者向后導(dǎo)航,每頁(yè)顯示特定數(shù)目的結(jié)果。

      

       但是有一個(gè)小問(wèn)題。使用DataGrid分頁(yè)時(shí)需要所有的數(shù)據(jù)都綁定到表格。例如,您的數(shù)據(jù)層會(huì)需要返回所有數(shù)據(jù),然后DataGrid要根據(jù)當(dāng)前頁(yè)填充所有要顯示的記錄。如果當(dāng)您在使用DataGrid分頁(yè)時(shí)返回了100,000條記錄,每次請(qǐng)求都會(huì)丟棄99,975條記錄(假設(shè)每頁(yè)的容量是25條記錄)。當(dāng)記錄的數(shù)量不斷增長(zhǎng)時(shí),應(yīng)用程序的性能會(huì)受到很大的影響,因?yàn)槊看握?qǐng)求都必須返回越來(lái)越多的數(shù)據(jù)。

 

       一個(gè)寫(xiě)出更好的分頁(yè)代碼的辦法是使用存儲(chǔ)過(guò)程。圖2顯示了一個(gè)示例存儲(chǔ)過(guò)程,它為Nothwind數(shù)據(jù)庫(kù)中的Orders數(shù)據(jù)表分頁(yè)。總的來(lái)說(shuō),在這里所有您需要做的就是傳入頁(yè)的索引和頁(yè)的容量。數(shù)據(jù)庫(kù)會(huì)計(jì)算出適當(dāng)?shù)慕Y(jié)果集然后返回它們。

 

 1CREATE PROCEDURE northwind_OrdersPaged
 2(
 3    @PageIndex int,
 4    @PageSize int
 5)
 6AS
 7BEGIN
 8DECLARE @PageLowerBound int
 9DECLARE @PageUpperBound int
10DECLARE @RowsToReturn int
11
12-- First set the rowcount
13SET @RowsToReturn = @PageSize * (@PageIndex + 1)
14SET ROWCOUNT @RowsToReturn
15
16-- Set the page bounds
17SET @PageLowerBound = @PageSize * @PageIndex
18SET @PageUpperBound = @PageLowerBound + @PageSize + 1
19
20-- Create a temp table to store the select results
21CREATE TABLE #PageIndex
22(
23    IndexId int IDENTITY (1, 1) NOT NULL,
24    OrderID int
25)
26
27-- Insert into the temp table
28INSERT INTO #PageIndex (OrderID)
29SELECT
30    OrderID
31FROM
32    Orders
33ORDER BY
34    OrderID DESC
35
36-- Return total count
37SELECT COUNT(OrderID) FROM Orders
38
39-- Return paged results
40SELECT
41    O.*
42FROM
43    Orders O,
44    #PageIndex PageIndex
45WHERE
46    O.OrderID = PageIndex.OrderID AND
47    PageIndex.IndexID > @PageLowerBound AND
48    PageIndex.IndexID < @PageUpperBound
49ORDER BY
50    PageIndex.IndexID
51
52END
53
54

 


       在社區(qū)服務(wù)期中,我們寫(xiě)了一個(gè)分頁(yè)服務(wù)端控件來(lái)做這些數(shù)據(jù)分頁(yè)。您會(huì)發(fā)現(xiàn)我在使用技巧1中討論過(guò)的思想,從一個(gè)存儲(chǔ)過(guò)程返回兩個(gè)結(jié)果集:紀(jì)錄總數(shù)和請(qǐng)求的數(shù)據(jù)。

 

       返回的記錄總數(shù)可以根據(jù)執(zhí)行的請(qǐng)求而有所不同。例如,一個(gè)WHERE分句可以用來(lái)約束返回的數(shù)據(jù)。我們必須知道要返回的記錄總數(shù),以計(jì)算要在分頁(yè)UI中顯示的總的頁(yè)數(shù)。例如,如果有1,000,000條總的記錄數(shù),而一個(gè)WHERE分句用來(lái)把這些記錄過(guò)濾為1,000條記錄,分頁(yè)邏輯需要知道總的記錄數(shù)來(lái)恰當(dāng)?shù)奶峤环猪?yè)UI。

 

技巧 3——連接池
 

       在您的Web應(yīng)用程序和SQL Server之間建立TCP連接會(huì)是一個(gè)昂貴的操作。Microsoft的開(kāi)發(fā)者們已經(jīng)利用連接池有一段時(shí)間了,這允許他們重用與數(shù)據(jù)庫(kù)的連接。與其為每個(gè)請(qǐng)求建立一個(gè)新的TCP連接,還不如只有在連接池中沒(méi)有一個(gè)可用的連接的時(shí)候才建立一個(gè)新的連接。當(dāng)連接關(guān)閉后,它返回到連接池中——它還保持著與數(shù)據(jù)庫(kù)的連接,而不是完全銷(xiāo)毀那個(gè)TCP連接。

      

       當(dāng)然您需要小心泄露的連接。總是關(guān)閉您的連接在您使用完它們時(shí)。我重復(fù)一遍:不管誰(shuí)說(shuō)了關(guān)于Microsoft .NET框架的垃圾回收機(jī)制的什么話,當(dāng)您使用完時(shí),您務(wù)必總是對(duì)您的連接顯式調(diào)用Close或者Dispose方法。不要相信通用語(yǔ)言運(yùn)行時(shí)(CLR)會(huì)在一個(gè)預(yù)定的時(shí)間為您清理和關(guān)閉您的連接。CLR會(huì)最終銷(xiāo)毀類(lèi)并且強(qiáng)迫連接關(guān)閉,但您不能保證什么時(shí)候在對(duì)象上的垃圾回收機(jī)制會(huì)真正執(zhí)行。

 

       要想使用連接池達(dá)到最佳效果,您需要遵循幾條規(guī)則。第一,打開(kāi)一個(gè)連接,完成工作,然后關(guān)閉連接。如果您不得不(最好應(yīng)用技巧1)為每個(gè)請(qǐng)求打開(kāi)和關(guān)閉幾次連接也是可以的,這比一直開(kāi)著連接然后把它傳遞給幾個(gè)不同的方法要好得多。第二,使用同一個(gè)連接字符串(如果您在使用集成身份認(rèn)證,當(dāng)然還需要有相同的線程標(biāo)識(shí))。如果您不使用同一個(gè)連接字符串,例如基于登錄的用戶的不同自定義連接字符串,您就不能得到連接池提供的相同的最優(yōu)值。而且如果您在模仿大量的用戶時(shí)使用了集成身份驗(yàn)證,您的連接池的效率也會(huì)降低很多。在嘗試跟蹤任何與連接池有關(guān)的性能問(wèn)題時(shí),.NET CLR數(shù)據(jù)性能計(jì)數(shù)器會(huì)很有用的。

 

       不論何時(shí)您的應(yīng)用程序連接一個(gè)資源,例如一個(gè)數(shù)據(jù)庫(kù),或者在另一個(gè)進(jìn)程中運(yùn)行,您都應(yīng)該通過(guò)把注意力集中到連接到資源所花費(fèi)的時(shí)間上,發(fā)送和接受數(shù)據(jù)花費(fèi)的時(shí)間,還有往返與數(shù)據(jù)庫(kù)的次數(shù)來(lái)進(jìn)行優(yōu)化。優(yōu)化您的應(yīng)用程序中的任何類(lèi)型的進(jìn)程跳轉(zhuǎn)(process hop)都是開(kāi)始達(dá)到更好性能的第一步。

 

       應(yīng)用層包含連接到您的數(shù)據(jù)層的邏輯,并且把數(shù)據(jù)轉(zhuǎn)換為有意義的類(lèi)實(shí)例和邏輯過(guò)程。例如,在社區(qū)服務(wù)器中,這里是您生成一個(gè)論壇或者線程集合,并且應(yīng)用業(yè)務(wù)規(guī)則例如許可的地方;更重要的是這里是執(zhí)行緩沖邏輯的地方。

 

技巧 4——ASP.NET緩沖API
      

       在您開(kāi)始編寫(xiě)應(yīng)用程序的第一行代碼之前要考慮的第一件事情是,架構(gòu)應(yīng)用層來(lái)最大化并且利用ASP.NET的緩存特性。

 

       如果您的組件運(yùn)行在一個(gè)ASP.NET應(yīng)用程序之中,您只需要在您的應(yīng)用程序項(xiàng)目中簡(jiǎn)單的引用System.Web.dll就可以了。當(dāng)您需要訪問(wèn)緩存時(shí),使用HttpRuntime.Cache屬性(這個(gè)對(duì)象也可以通過(guò)Page.Cache和HttpContext.Cache來(lái)訪問(wèn))。

 

       使用緩存數(shù)據(jù)有幾條原則。第一,如果數(shù)據(jù)可以多次使用,那么緩存它就是一個(gè)好的選擇。第二,如果數(shù)據(jù)是通用的而不是給特定的請(qǐng)求或者用戶使用的,那么緩存它就是一個(gè)非常好的選擇。如果數(shù)據(jù)是用戶或者請(qǐng)求特定的,但是他的生存期是很長(zhǎng)的,那么它也可以被緩存,但是可能不會(huì)經(jīng)常使用到。第三,一個(gè)經(jīng)常被忽視的原則是,有時(shí)候您可以緩存的太多了。通常在一臺(tái)x86計(jì)算機(jī)上,為了減少發(fā)生內(nèi)存不足(out-of-memory)錯(cuò)誤的可能性,您會(huì)希望運(yùn)行一個(gè)使用不超過(guò)800MB私有字節(jié)的進(jìn)程。因此,緩存應(yīng)該受到限制。換句話說(shuō),您可能需要重新使用一次計(jì)算的結(jié)果,但是如果那個(gè)計(jì)算需要十個(gè)參數(shù),您可能需要嘗試緩存10個(gè)排列,而這可能會(huì)給您帶來(lái)麻煩。由于過(guò)度緩存引起的內(nèi)存不足錯(cuò)誤是ASP.NET中最常見(jiàn)的,特別是對(duì)于大數(shù)據(jù)集的情況。

      

       緩存有幾個(gè)極佳的功能,您需要對(duì)它們有所了解。首先,緩存會(huì)實(shí)現(xiàn)最近最少使用的算法,使得 ASP.NET 能夠在內(nèi)存運(yùn)行效率較低的情況下強(qiáng)制緩存清除——從緩存自動(dòng)刪除未使用過(guò)的項(xiàng)目。第二,緩存支持可以強(qiáng)制失效的過(guò)期依賴(lài)項(xiàng)。這些依賴(lài)項(xiàng)包括時(shí)間、鍵和文件。時(shí)間經(jīng)常會(huì)用到,但是對(duì)于 ASP.NET 2.0,引入了一個(gè)功能更強(qiáng)的新失效類(lèi)型:數(shù)據(jù)庫(kù)緩存失效。它指的是當(dāng)數(shù)據(jù)庫(kù)中的數(shù)據(jù)發(fā)生變化時(shí)自動(dòng)刪除緩存中的項(xiàng)。有關(guān)數(shù)據(jù)庫(kù)緩存失效的詳細(xì)信息,請(qǐng)參閱 MSDN Magazine 2004 年 7 月的 Dino Esposito Cutting Edge 專(zhuān)欄。要了解緩存的體系結(jié)構(gòu),請(qǐng)參閱圖 3。

 

技巧 5 — 每請(qǐng)求緩存
 

 

       在本文前面部分,我提到了對(duì)經(jīng)常遍歷代碼路徑的一些小改善可以獲得較大的整體性能收益。對(duì)于這些小改善,其中有一個(gè)絕對(duì)是我的最?lèi)?ài),我將其稱(chēng)之為“每請(qǐng)求緩存”。

 

       緩存 API 的設(shè)計(jì)目的是為了將數(shù)據(jù)緩存較長(zhǎng)的一段時(shí)間,或者緩存至滿足某些條件時(shí),但每請(qǐng)求緩存則意味著只將數(shù)據(jù)緩存為該請(qǐng)求的持續(xù)時(shí)間。對(duì)于每個(gè)請(qǐng)求,要經(jīng)常訪問(wèn)某個(gè)特定的代碼路徑,但是數(shù)據(jù)卻只需提取、應(yīng)用、修改或更新一次。這聽(tīng)起來(lái)有些理論化,那么我們來(lái)舉一個(gè)具體的示例。

 

       在社區(qū)服務(wù)器的論壇應(yīng)用程序中,頁(yè)面上使用的每個(gè)服務(wù)器控件都需要個(gè)性化的數(shù)據(jù)來(lái)確定使用什么外觀、使用什么樣式表,以及其他個(gè)性化數(shù)據(jù)。這些數(shù)據(jù)中有些可以長(zhǎng)期緩存,但是有些數(shù)據(jù)卻只針對(duì)每個(gè)請(qǐng)求提取一次,然后在執(zhí)行該請(qǐng)求期間對(duì)其重用多次,如要用于控件的外觀。

 

       為了達(dá)到每請(qǐng)求緩存,請(qǐng)使用 ASP.NET HttpContext。對(duì)于每個(gè)請(qǐng)求,都會(huì)創(chuàng)建一個(gè) HttpContext 實(shí)例,在該請(qǐng)求期間從 HttpContext.Current 屬性的任何位置都可訪問(wèn)該實(shí)例。該 HttpContext 類(lèi)具有一個(gè)特殊的 Items 集合屬性;添加到此 Items 集合的對(duì)象和數(shù)據(jù)只在該請(qǐng)求持續(xù)期間內(nèi)進(jìn)行緩存。正如您可以使用緩存來(lái)存儲(chǔ)經(jīng)常訪問(wèn)的數(shù)據(jù)一樣,您也可以使用 HttpContext.Items 來(lái)存儲(chǔ)只基于每個(gè)請(qǐng)求使用的數(shù)據(jù)。它背后的邏輯非常簡(jiǎn)單:數(shù)據(jù)在它不存在的時(shí)候添加到 HttpContext.Items 集合,在后來(lái)的查找中,只是返回 HttpContext.Items 中的數(shù)據(jù)。

 

技巧 6 — 后臺(tái)處理
 

 

       通往代碼的路徑應(yīng)該盡可能快速,是嗎?可能有時(shí)您會(huì)發(fā)現(xiàn)您正在執(zhí)行的針對(duì)每個(gè)請(qǐng)求執(zhí)行的或者每 n 個(gè)請(qǐng)求執(zhí)行一次的任務(wù)所需資源非常多。發(fā)送電子郵件或者分析和驗(yàn)證傳入數(shù)據(jù)就是這樣的一些例子。

 

       剖析 ASP.NET Forums 1.0 并重新構(gòu)建組成社區(qū)服務(wù)器的內(nèi)容時(shí),我們發(fā)現(xiàn)發(fā)布新帖子的代碼路徑非常慢。每次發(fā)布新帖子的時(shí)候,應(yīng)用程序首先需要確保沒(méi)有重復(fù)的帖子,然后必須使用“壞詞”篩選器分析該帖子,分析帖子的字符圖釋?zhuān)瑢?duì)帖子添加標(biāo)記并進(jìn)行索引,請(qǐng)求時(shí)將帖子添加到合適的隊(duì)列,驗(yàn)證附件,最終在帖子發(fā)布之后,立即向所有訂閱者發(fā)出電子郵件通知。很清楚,這涉及很多操作。

 

       經(jīng)研究發(fā)現(xiàn),大多數(shù)時(shí)間都花在了索引邏輯和發(fā)送電子郵件上。對(duì)帖子進(jìn)行索引是一個(gè)非常耗時(shí)的操作,人們發(fā)現(xiàn)內(nèi)置的 System.Web.Mail 功能要連接 SMTP 服務(wù)器,然后連續(xù)發(fā)送電子郵件。當(dāng)某個(gè)特定帖子或主題領(lǐng)域的訂閱者數(shù)量增加時(shí),執(zhí)行 AddPost 功能所需的時(shí)間也越來(lái)越長(zhǎng)。

 

       并不需要針對(duì)每個(gè)請(qǐng)求都進(jìn)行電子郵件索引。理想情況下,我們想要將此操作進(jìn)行批處理,一次索引 25 個(gè)帖子或者每五分鐘發(fā)送一次所有電子郵件。我們決定使用我曾經(jīng)用于對(duì)數(shù)據(jù)緩存失效進(jìn)行原型設(shè)計(jì)的代碼,這個(gè)失效是最終被包含進(jìn)了Visual Studio 2005之中。

 

       System.Threading 命名空間中的 Timer 類(lèi)非常有用,但是在 .NET Framework 中不是很有名,至少對(duì)于 Web 開(kāi)發(fā)人員來(lái)說(shuō)是這樣。創(chuàng)建之后,這個(gè) Timer 類(lèi)將以一個(gè)可配置的間隔針對(duì) ThreadPool 中的某個(gè)線程調(diào)用指定的回調(diào)。這就表示,您可以對(duì)代碼進(jìn)行設(shè)置,使其能夠在沒(méi)有對(duì) ASP.NET 應(yīng)用程序進(jìn)行傳入請(qǐng)求的情況下得以執(zhí)行,這是后臺(tái)處理的理想情況。您還可以在此后臺(tái)進(jìn)程中執(zhí)行如索引或發(fā)送電子郵件之類(lèi)的操作。

 

       但是,這一技術(shù)有幾個(gè)問(wèn)題。如果應(yīng)用程序域卸載,該計(jì)時(shí)器實(shí)例將停止激發(fā)事件。另外,因?yàn)?CLR 對(duì)于每個(gè)進(jìn)程的線程數(shù)量具有一個(gè)硬性標(biāo)準(zhǔn),所以在負(fù)載很重的服務(wù)器可能會(huì)出現(xiàn)這樣的情形:其中的計(jì)時(shí)器可能不能保證線程繼續(xù)完成操作,并且在某種程度上可能會(huì)造成延遲。ASP.NET 通過(guò)在進(jìn)程中保留一定數(shù)量的可用線程,并且僅使用總線程的一部分用于請(qǐng)求處理,試圖將上述情況發(fā)生的機(jī)會(huì)降到最低。但是,如果您具有很多異步操作時(shí),這可能就是一個(gè)問(wèn)題了。

 

       這里沒(méi)有足夠的空間來(lái)放置該代碼,但是您可以下載一個(gè)容易理解的示例,網(wǎng)址是www.rob-howard.net。請(qǐng)了解一下 Blackbelt TechEd 2004 演示中的幻燈片和演示。

 

技巧 7 — 頁(yè)輸出緩存和代理服務(wù)器
 

 

       ASP.NET 是您的表示層(或者說(shuō)應(yīng)該是您的表示層);它由頁(yè)、用戶控件、服務(wù)器控件(HttpHandlers 和 HttpModules)以及它們生成的內(nèi)容組成。如果您具有一個(gè) ASP.NET 頁(yè),它會(huì)生成輸出(HTML、xml、圖像或任何其他數(shù)據(jù)),并且您針對(duì)每個(gè)請(qǐng)求運(yùn)行此代碼時(shí),它都會(huì)生成相同的輸出,那么您就擁有一個(gè)可用于頁(yè)輸出緩存的絕佳備選內(nèi)容。

 

       通過(guò)將下面這行內(nèi)容添加頁(yè)的最上端:

 

<%@ Page OutputCache VaryByParams="none" Duration="60" %>
 

 

       您就可以高效地為此頁(yè)生成一次輸出,然后對(duì)它進(jìn)行多次重用,時(shí)間最長(zhǎng)為 60 秒,此時(shí)該頁(yè)將重新執(zhí)行,輸出也將再一次添加到 ASP.NET 緩存。通過(guò)使用一些低級(jí)別可編程API 也可以完成此行為。對(duì)于輸出緩存有幾個(gè)可配置的設(shè)置,如剛剛講到的 VaryByParams 屬性。VaryByParams 剛好被請(qǐng)求到,但還允許您指定 HTTP GET 或 HTTP POST 參數(shù)來(lái)更改緩存項(xiàng)。例如,只需設(shè)置 VaryByParam="Report" 即可對(duì) default.aspx?Report=1 或 default.aspx?Report=2 進(jìn)行輸出緩存。通過(guò)指定一個(gè)以分號(hào)分隔的列表,還可以指定其他參數(shù)。

 

       很多人還沒(méi)有意識(shí)到當(dāng)使用了輸出緩存之后,ASP.NET 頁(yè)也會(huì)生成一些向下流到緩存服務(wù)器的 HTTP 標(biāo)題頭,如 Microsoft Internet Security 和 Acceleration Server 或 Akamai 使用的標(biāo)題頭。設(shè)置了 HTTP 緩存表題頭之后,可以在這些網(wǎng)絡(luò)資源上對(duì)文檔進(jìn)行緩存,客戶端請(qǐng)求也可在不必返回原始服務(wù)器的情況下得以滿足。

 

       因此,使用頁(yè)輸出緩存不會(huì)使得您的應(yīng)用程序效率更高,但是它可能會(huì)減少服務(wù)器上的負(fù)載,因?yàn)橄滦辛骶彺婕夹g(shù)會(huì)緩存文檔。當(dāng)然,這只能是匿名內(nèi)容;一旦它成為下行流之后,您就再也不會(huì)看到這些請(qǐng)求,并且再也無(wú)法執(zhí)行身份驗(yàn)證以阻止對(duì)它的訪問(wèn)了。

 

技巧 8 — 運(yùn)行 IIS 6.0(哪怕只為了使用內(nèi)核緩存也好)
 

 

       如果您未運(yùn)行 IIS 6.0 (Windows Server 2003),那么您就錯(cuò)過(guò)了 Microsoft Web 服務(wù)器中的一些很好的性能增強(qiáng)。在技巧 7 中,我討論了輸出緩存。在 IIS 5.0 中,請(qǐng)求是通過(guò) IIS 然后進(jìn)入 ASP.NET 的。涉及到緩存時(shí),ASP.NET 中的 HttpModule 會(huì)接收該請(qǐng)求,并返回緩存中的內(nèi)容。

 

       如果您正在使用 IIS 6.0,就會(huì)發(fā)現(xiàn)一個(gè)很好的小功能,稱(chēng)為內(nèi)核緩存,它不需要對(duì) ASP.NET 進(jìn)行任何代碼更改。當(dāng)請(qǐng)求由 ASP.NET 進(jìn)行輸出緩存時(shí),IIS 內(nèi)核緩存會(huì)接收緩存數(shù)據(jù)的一個(gè)副本。當(dāng)請(qǐng)求來(lái)自網(wǎng)絡(luò)驅(qū)動(dòng)程序時(shí),內(nèi)核級(jí)別的驅(qū)動(dòng)程序(無(wú)上下文切換到用戶模式)就會(huì)接收該請(qǐng)求,如果經(jīng)過(guò)了緩存,則會(huì)將緩存的數(shù)據(jù)刷新到響應(yīng),然后完成執(zhí)行。這就表示,當(dāng)您將內(nèi)核模式緩存與 IIS 和 ASP.NET 輸出緩存一起使用時(shí),就會(huì)看到令人不敢相信的性能結(jié)果。在 ASP.NET 的 Visual Studio 2005 開(kāi)發(fā)過(guò)程中,我一度是負(fù)責(zé) ASP.NET 性能的開(kāi)發(fā)經(jīng)理。開(kāi)發(fā)人員完成具體工作,但是我要看到每天進(jìn)行的所有報(bào)告。內(nèi)核模式緩存結(jié)果總是最有意思的。最常見(jiàn)的特征是網(wǎng)絡(luò)充滿了請(qǐng)求/響應(yīng),而 IIS 運(yùn)行時(shí)的 CPU 使用率只有大約 5%。這太令人震驚了!當(dāng)然使用 IIS 6.0 還有一些其他原因,但是內(nèi)核模式緩存是其中最明顯的一個(gè)。

 

技巧 9 — 使用 Gzip 壓縮
 

 

       雖然使用 gzip 并不一定是服務(wù)器性能技巧(因?yàn)槟赡軙?huì)看到 CPU 使用率的提高),但是使用 gzip 壓縮可以減少服務(wù)器發(fā)送的字節(jié)數(shù)量。這就使人們覺(jué)得頁(yè)速度加快了,并且還減少了帶寬的用量。根據(jù)所發(fā)送數(shù)據(jù)、可以壓縮的程度以及客戶端瀏覽器是否支持(IIS 只會(huì)向支持 gzip 壓縮的客戶端發(fā)送經(jīng)過(guò) gzip 壓縮的內(nèi)容,如 Internet Explorer 6.0 和 Firefox),您的服務(wù)器每秒可以服務(wù)于更多的請(qǐng)求。實(shí)際上,幾乎每當(dāng)您減少所返回?cái)?shù)據(jù)的數(shù)量時(shí),都會(huì)增加每秒請(qǐng)求數(shù)。

 

       Gzip 壓縮已經(jīng)內(nèi)置到 IIS 6.0 中,并且其性能比 IIS 5.0 中使用的 gzip 壓縮要好的多,這是好消息。但不幸的是,當(dāng)嘗試在 IIS 6.0 中打開(kāi) gzip 壓縮時(shí),您可能無(wú)法在 IIS 的屬性對(duì)話中找到該設(shè)置。IIS 小組在該服務(wù)器中置入了卓越的 gzip 功能,但是忘了包括一個(gè)用于啟用該功能的管理 UI。要啟用 gzip 壓縮,您必須深入到 IIS 6.0 的 XML 配置設(shè)置內(nèi)部(這樣不會(huì)引起心臟虛弱)。順便提一句,這歸功于 OrcsWeb 的 Scott Forsyth,他幫助我提出了在 OrcsWeb 上宿主的 www.asp.net 服務(wù)器的這個(gè)問(wèn)題。

 

       本文就不講述步驟了,請(qǐng)閱讀 Brad Wilson 的文章,網(wǎng)址是 IIS6 Compression。還有一篇有關(guān)為 ASPX 啟用壓縮的知識(shí)庫(kù)文章,網(wǎng)址是 Enable ASPX Compression in IIS。但是您應(yīng)該注意,由于一些實(shí)施細(xì)節(jié),IIS 6.0 中不能同時(shí)存在動(dòng)態(tài)壓縮和內(nèi)核緩存。

 

技巧 10 — 服務(wù)器控件視圖狀態(tài)
 

 

       視圖狀態(tài)是一個(gè)有趣的名稱(chēng),用于表示在所生成頁(yè)的隱藏輸出字段中存儲(chǔ)一些狀態(tài)數(shù)據(jù)的 ASP.NET。當(dāng)該頁(yè)發(fā)回服務(wù)器時(shí),服務(wù)器可以分析、驗(yàn)證、并將此視圖狀態(tài)數(shù)據(jù)應(yīng)用回該頁(yè)的控件樹(shù)。視圖狀態(tài)是一個(gè)非常強(qiáng)大的功能,因?yàn)樗试S狀態(tài)與客戶端一起保持,并且它不需要 cookie 或服務(wù)器內(nèi)存即可保存此狀態(tài)。很多 ASP.NET 服務(wù)器控件都使用視圖狀態(tài)來(lái)保持在與頁(yè)元素進(jìn)行交互期間創(chuàng)建的設(shè)置,例如保存對(duì)數(shù)據(jù)進(jìn)行分頁(yè)時(shí)顯示的當(dāng)前頁(yè)。

 

       然而使用視圖狀態(tài)也有一些缺點(diǎn)。首先,當(dāng)頁(yè)被服務(wù)或被請(qǐng)求時(shí),它都會(huì)增加頁(yè)的總負(fù)載。對(duì)發(fā)回服務(wù)器的視圖狀態(tài)數(shù)據(jù)進(jìn)行序列化或取消序列化時(shí),也會(huì)發(fā)生額外的開(kāi)銷(xiāo)。最后,視圖狀態(tài)會(huì)增加服務(wù)器上的內(nèi)存分配。

 

       幾個(gè)服務(wù)器控件有過(guò)度使用視圖狀態(tài)的趨勢(shì),即使在并不需要的情況下也要使用它,其中最著名的是 DataGrid。ViewState 屬性的默認(rèn)行為是啟用,但是如果您不需要,則可以在控件或頁(yè)級(jí)別關(guān)閉。在控件內(nèi),只需將 EnableViewState 屬性設(shè)置為 false,或者在頁(yè)中使用下列設(shè)置即可對(duì)其進(jìn)行全局設(shè)置:

 

<%@ Page EnableViewState="false" %>
 

       如果您不發(fā)回頁(yè),或者總是針對(duì)每個(gè)請(qǐng)求重新生成頁(yè)上的控件,則應(yīng)該在頁(yè)級(jí)別禁用視圖狀態(tài)。

 
小結(jié)
 

       我為您講述了一些我認(rèn)為在編寫(xiě)高性能 ASP.NET 應(yīng)用程序時(shí)有所幫助的技巧。正如我在本文前面部分提到的那樣,這是一個(gè)初步指南,并不是 ASP.NET 性能的最后定論。(有關(guān)改善 ASP.NET 應(yīng)用程序性能的信息,請(qǐng)參閱 Improving ASP.NET Performance。)只有通過(guò)自己的親身體驗(yàn)才能找出解決具體性能問(wèn)題的最好方法。但是,在您的旅程中,這些技巧應(yīng)該會(huì)為您提供一些好的指南。在軟件開(kāi)發(fā)中,幾乎沒(méi)有絕對(duì)的東西;每個(gè)應(yīng)用程序都是唯一的。

 

請(qǐng)參閱提要欄“Common Performance Myths”。

 

Rob Howard 是 Telligent Systems 的創(chuàng)始人,專(zhuān)門(mén)從事高性能 Web 應(yīng)用程序、知識(shí)庫(kù)管理和協(xié)作系統(tǒng)方面的工作。Rob 以前受雇于 Microsoft,他在那里幫助設(shè)計(jì)了 ASP.NET 1.0、1.1 和 2.0 的基礎(chǔ)結(jié)構(gòu)。要聯(lián)系 Rob,請(qǐng)?jiān)L問(wèn) rhoward@telligentsystems.com


 原文鏈接:http://msdn.microsoft.com/msdnmag/issues/05/01/ASPNETPerformance/default.aspx


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 新闻| 龙泉市| 临沂市| 绵竹市| 深州市| 宜宾市| 安图县| 咸宁市| 稻城县| 光泽县| 山西省| 大埔县| 调兵山市| 安溪县| 满洲里市| 星子县| 北碚区| 长沙市| 东乌珠穆沁旗| 获嘉县| 延长县| 宁津县| 临沧市| 涞源县| 天镇县| 平凉市| 昌黎县| 盐城市| 长寿区| 永修县| 恭城| 淳化县| 南阳市| 娱乐| 乐安县| 会宁县| 仪征市| 高碑店市| 突泉县| 福鼎市| 特克斯县|