這篇文章討論了:
·一般ASP.NET效能的秘密
·能提高ASP.NET表現的有用的技巧和竅門
·在ASP.NET中使用資料庫的建議
·ASP.NET中的緩衝和幕後處理
使用ASP.NET編寫一個Web應用程式是難以置信的簡單的。太簡單了,以至於很多研發者都不花費時間來構建他們的應用程式來達到很好的表現。在這篇文章裡,我將為編寫高效能的Web應用程式推薦10個技巧。我不會講我的論述局限於ASP.NET應用程式,因為ASP.NET應用程式只是Web應用程式的一個子集而已。這篇文章不會是針對最佳化Web應用程式的效能的權威性指導——一本完整的書能夠很容易的做到這一點。相反,我們應該把這篇文章當成一個好的起點。
在成為一個工作狂以前,我會經常去攀岩。在做任何攀岩活動之前,我更願意看看旅行指南裡面的路線,再讀讀那些曾到過峰頂的人做的推薦。但是,不管旅行指南寫的有多好,在嘗試一個有挑戰性的目標之前,您都需要有實際的攀岩經驗。和之相似,當您面臨修複效能問題或運行一個高輸送量網站的問題時,您只能學習如何編寫高效能 Web 應用程式程式。
我的個人經驗來自在微軟的ASP.NET團隊中擔任過一名基礎程式經理的經曆,維護和管理www.asp.net,更有協助構架Community Server,他是幾個著名的ASP.NET應用程式(ASP.NET Forums,.Text,和串連到一個平台的nGallery)的下一個版本。我相信這些曾協助過我技巧中的一些也會對您有用的。
您應該考慮把您的應用程式分離為幾個邏輯層次。您可能已聽說過3層(或n層)體繫結構。這些通常都是規定的結構模式,他們將業務和(或)硬體從物理上進行了功能劃分。假如系統需要更大的規模,更多的硬體能夠輕鬆的加進來。然而,那會產生一個和業務和機器跳躍相關聯的效能下降,因此我們應該避免他。所以只要可能,盡量在同一個應用程式中運行ASP.NET頁面和頁面的相關組件。
因為代碼的分離和層次之間的邊界,使用Web服務或遠端會降低效能20%甚至更多。
資料層有點和眾不同,因為通常情況下,最好具備專用於資料庫的硬體。然而,然而進程跳躍到資料庫的成本依然很高,因此在資料層的效能是您最佳化代碼時應該首先考慮的。
在投入到修複您的應用程式的效能問題之前,確保您要先分析您的應用程式來發現問題的根源所在。關鍵效能計數器(例如那個指示在執行垃圾收集過程中花費的時間百分比的計數器)在找出應用程式在哪裡花費了主要的時間時也是很有用的。雖然那些花費時間的地方經常是不那麼直觀的。
在這篇文章中我討論了兩種改進效能的方法:大塊的最佳化,例如使用ASP.NET緩衝,更有小塊的最佳化,他們經常重複出現。這些小塊的最佳化有時是最有意思的。您對代碼的一個小的修改會被調用成千上萬次。對大塊的最佳化,您可能會發現整個的效能有了一個大的飛躍。對小塊的最佳化,您可能會縮減了對一個給定請求的幾微秒的時間,但是假如把每天的任何的請求累積起來,效能就會得到一個意想不到的改進。
資料層中的效能
當您要開始最佳化一個應用程式的效能的時候,有一個決定性的測試您能夠優先考慮使用:代碼是否要訪問資料庫?假如是,多長時間訪問一次?注意這個測試也能夠應用到那些使用Web服務或遠端控制的代碼中,但是我不會在這篇文章中涉及那些內容。
假如在您的代碼中的某個代碼路徑中需要一個資料庫請求,而您發現其他地方您想要優先最佳化,例如字串操作,那麼停下來然後先執行關鍵性的測試。除非您有一個效能實在糟糕的問題要處理,否則您的時間會得到更好的利用,假如您把時間花在最佳化資料庫連接的時間,返回的資料量,更有您作的往返資料庫的操作中。
現在我已總體介紹了相關的資訊,下面讓我們看看10條幫您的應用程式表現更好的技巧。我會從那些對改善效能效果最明顯的地方開始說。
技巧 1——返回多個結果集
查看一下您的資料庫代碼,看看您是否有訪問資料庫多於一次的請求路徑(request paths)。每個這樣的往返都回降低您的應用程式每秒能夠服務的請求的數量。通過在一次資料庫請求中返回多個結果集,您能夠減少資料庫通訊消耗的總時間。在您減少了資料庫伺服器管理的請求之後,您也會使您的系統更具可升級性。
一般您能夠使用動態SQL語句來返回多個結果集,我更喜歡用預存程序。是否應該把商務邏輯放在預存程序中是存在爭議的,但我認為假如一個預存程序中的邏輯能夠限制返回的資料(減少資料集的大小,花在網路連接上的時間,並且無需過濾邏輯層的資料),那他就是好東西。
使用一個SqlCommand執行個體和他的ExecuteReader方法來產生強型別的業務類,您能夠通過調用NextResult讓結果集指標向前移動。圖1展示了一個使用定義的類產生幾個ArrayList的樣本會話。只從資料庫返回您需要的資料會顯著地減少您伺服器上的記憶體申請。
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——分頁資料訪問
ASP.NET的DataGrid提供了一個很棒的能力:對資料分頁的支援。當在DataGrid中配置了分頁,那麼將一次顯示一個特定數目的結果。此外,用來在結果之間導航的分頁UI也會在DataGrid的底部顯示出來。分頁UI允許您在顯示的資料之間向前置航或向後導航,每頁顯示特定數目的結果。
但是有一個小問題。使用DataGrid分頁時需要任何的資料都綁定到表格。例如,您的資料層會需要返回任何資料,然後DataGrid要根據當前頁填充任何要顯示的記錄。假如當您在使用DataGrid分頁時返回了100,000條記錄,每次請求都會丟棄99,975條記錄(假設每頁的容量是25條記錄)。當記錄的數量不斷增長時,應用程式的效能會受到很大的影響,因為每次請求都必須返回越來越多的資料。
一個寫出更好的分頁代碼的辦法是使用預存程序。圖2顯示了一個樣本預存程序,他為Nothwind資料庫中的Orders資料表分頁。總的來說,在這裡任何您需要做的就是傳入頁的索引和頁的容量。資料庫會計算出適當的結果集然後返回他們。
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
在社區服務期中,我們寫了一個分頁服務端控制項來做這些資料分頁。您會發現我在提示1中討論過的思想,從一個預存程序返回兩個結果集:紀錄總數和請求的資料。
返回的記錄總數能夠根據執行的請求而有所不同。例如,一個WHERE分句能夠用來約束返回的資料。我們必須知道要返回的記錄總數,以計算要在分頁UI中顯示的總的頁數。例如,假如有1,000,000條總的記錄數,而一個WHERE分句用來把這些記錄過濾為1,000條記錄,分頁邏輯需要知道總的記錄數來恰當的提交分頁UI。
技巧 3——串連池
在您的Web應用程式和SQL Server之間建立TCP串連會是個昂貴的操作。Microsoft的研發者們已利用串連池有一段時間了,這允許他們重用和資料庫的串連。和其為每個請求建立一個新的TCP串連,還不如只有在串連池中沒有一個可用的串連的時候才建立一個新的串連。當串連關閉後,他返回到串連池中——他還保持著和資料庫的串連,而不是完全銷毀那個TCP串連。
當然您需要小心泄露的串連。總是關閉您的串連在您使用完他們時。我重複一遍:不管誰說了關於Microsoft .NET架構的記憶體回收機制的什麼話,當您使用完時,您務必總是對您的串連顯式調用Close或Dispose方法。不要相信通用語言運行時(CLR)會在一個預定的時間為您清理和關閉您的串連。CLR會最終銷毀類並且強迫串連關閉,但您不能確保什麼時候在對象上的記憶體回收機制會真正執行。
要想使用串連池達到最好效果,您需要遵循幾條規則。第一,開啟一個串連,完成工作,然後關閉串連。假如您不得不(最好應用技巧1)為每個請求開啟和關閉幾次串連也是能夠的,這比一直開著串連然後把他傳遞給幾個不同的方法要好得多。第二,使用同一個連接字串(假如您在使用整合身份認證,當然還需要有相同的線程標識)。假如您不使用同一個連接字串,例如基於登入的使用者的不同自訂連接字串,您就不能得到串連池提供的相同的最優值。而且假如您在模仿大量的使用者時使用了整合身分識別驗證,您的串連池的效率也會降低很多。在嘗試跟蹤任何和串連池有關的效能問題時,.NET CLR資料效能計數器會很有用的。
不論何時您的應用程式串連一個資源,例如一個資料庫,或在另一個進程中運行,您都應該通過把注意力集中到串連到資源所花費的時間上,發送和接受資料花費的時間,更有往返和資料庫的次數來進行最佳化。最佳化您的應用程式中的任何類型的進程跳轉(process hop)都是開始達到更好效能的第一步。
應用程式層包含串連到您的資料層的邏輯,並且把資料轉換為有意義的類執行個體和邏輯過程。例如,在社區伺服器中,這裡是您產生一個論壇或線程集合,並且應用商務規則例如許可的地方;更重要的是這裡是執行緩衝邏輯的地方。
技巧 4——ASP.NET緩衝API
在您開始編寫應用程式的第一行代碼之前要考慮的第一件事情是,架構應用程式層來最大化並且利用ASP.NET的緩衝特性。
假如您的組件運行在一個ASP.NET應用程式之中,您只需要在您的應用程式項目中簡單的引用System.Web.dll就能夠了。當您需要訪問緩衝時,使用HttpRuntime.Cache屬性(這個對象也能夠通過Page.Cache和HttpContext.Cache來訪問)。
使用快取資料有幾條原則。第一,假如資料能夠多次使用,那麼緩衝他就是個好的選擇。第二,假如資料是通用的而不是給特定的請求或使用者使用的,那麼緩衝他就是個很好的選擇。假如資料是使用者或請求特定的,但是他的生存期是很長的,那麼他也能夠被緩衝,但是可能不會經常使用到。第三,一個經常被忽視的原則是,有時候您能夠緩衝的太多了。通常在一台x86電腦上,為了減少發生記憶體不足(out-of-memory)錯誤的可能性,您會希望運行一個使用不超過800MB私人位元組的進程。因此,緩衝應該受到限制。換句話說,您可能需要重新使用一次計算的結果,但是假如那個計算需要十個參數,您可能需要嘗試緩衝10個排列,而這可能會給您帶來麻煩。由於過度緩衝引起的記憶體不足錯誤是ASP.NET中最常見的,特別是對於大資料集的情況。
緩衝有幾個極佳的功能,您需要對他們有所瞭解。首先,緩衝會實現最近最少使用的演算法,使得 ASP.NET 能夠在記憶體運行效率較低的情況下強制緩衝清除——從緩衝自動刪除未使用過的項目。第二,緩衝支援能夠強制失效的到期依賴項。這些依賴項包括時間、鍵和文檔。時間經常會用到,但是對於 ASP.NET 2.0,引入了一個功能更強的新失效類型:資料庫緩衝失效。他指的是當資料庫中的資料發生變化時自動刪除緩衝中的項。有關資料庫緩衝失效的周詳資訊,請參閱 MSDN Magazine 2004 年 7 月的 Dino Esposito Cutting Edge 專欄。要瞭解緩衝的體繫結構,請參閱圖 3。
技巧 5 — 每請求緩衝
在本文前面部分,我提到了對經常遍曆代碼路徑的一些小改善能夠獲得較大的整體效能收益。對於這些小改善,其中有一個絕對是我的最愛,我將其稱之為“每請求緩衝”。
緩衝 API 的設計目的是為了將資料緩衝較長的一段時間,或緩衝至滿足某些條件時,但每請求緩衝則意味著只將資料緩衝為該請求的期間。對於每個請求,要經常訪問某個特定的代碼路徑,但是資料卻只需提取、應用、修改或更新一次。這聽起來有些理論化,那麼我們來舉一個具體的樣本。
在社區伺服器的論壇應用程式中,頁面上使用的每個伺服器控制項都需要個人化的資料來確定使用什麼外觀、使用什麼樣式表,連同其他個人化資料。這些資料中有些能夠長期緩衝,但是有些資料卻只針對每個請求提取一次,然後在執行該請求期間對其重用多次,如要用於控制項的外觀。
為了達到每請求緩衝,請使用 ASP.NET HttpContext。對於每個請求,都會建立一個 HttpContext 執行個體,在該請求期間從 HttpContext.Current 屬性的任何位置都可訪問該執行個體。該 HttpContext 類具備一個特別的 Items 集合屬性;添加到此 Items 集合的對象和資料只在該請求持續期間內進行緩衝。正如您能夠使用緩衝來儲存經常訪問的資料相同,您也能夠使用 HttpContext.Items 來儲存只基於每個請求使用的資料。他背後的邏輯很簡單:資料在他不存在的時候添加到 HttpContext.Items 集合,在後來的尋找中,只是返回 HttpContext.Items 中的資料。
技巧 6 — 幕後處理
通往代碼的路徑應該儘可能快速,是嗎?可能有時您會發現您正在執行的針對每個請求執行的或每 n 個請求執行一次的任務所需資源很多。寄送電子郵件或分析和驗證傳入資料就是這樣的一些例子。
剖析 ASP.NET Forums 1.0 並重新構建組成社區伺服器的內容時,我們發現發布新文章的代碼路徑很慢。每次發布新文章的時候,應用程式首先需要確保沒有重複的文章,然後必須使用“壞詞”篩選器分析該文章,分析文章的字元圖釋,對文章委任標記並進行索引,請求時將文章添加到合適的隊列,驗證附件,最終在文章發布之後,立即向任何訂閱者發出電子郵件通知。很清楚,這涉及很多操作。
經研究發現,大多數時間都花在了索引邏輯和寄送電子郵件上。對文章進行索引是個很耗時的操作,人們發現內建的 System.Web.Mail 功能要串連 SMTP 伺服器,然後連續寄送電子郵件。當某個特定文章或主題領域的訂閱者數量增加時,執行 AddPost 功能所需的時間也越來越長。
並無需針對每個請求都進行電子郵件索引。理想情況下,我們想要將此操作進行批處理,一次索引 25 個文章或每五分鐘發送一次任何電子郵件。我們決定使用我曾用於對資料緩衝失效進行原型設計的代碼,這個失效是最終被包含進了Visual Studio 2005之中。
System.Threading 命名空間中的 Timer 類很有用,但是在 .NET Framework 中不是很有名,至少對於 Web 研發人員來說是這樣。建立之後,這個 Timer 類將以一個可配置的間隔針對 ThreadPool 中的某個線程調用指定的回調。這就表示,您能夠對代碼進行配置,使其能夠在沒有對 ASP.NET 應用程式進行傳入請求的情況下得以執行,這是幕後處理的理想情況。您還能夠在此後台進程中執行如索引或寄送電子郵件之類的操作。
但是,這一技術有幾個問題。假如應用程式域卸載,該計時器執行個體將停止激發事件。另外,因為 CLR 對於每個進程的線程數量具備一個硬性標準,所以在負載很重的伺服器可能會出現這樣的情形:其中的計時器可能不能確保線程繼續完成操作,並且在某種程度上可能會造成延遲。ASP.NET 通過在進程中保留一定數量的可用線程,並且僅使用匯流排程的一部分用於請求處理,試圖將上述情況發生的機會降到最低。但是,假如您具備很多非同步作業時,這可能就是個問題了。
這裡沒有足夠的空間來放置該代碼,但是您能夠下載一個容易理解的樣本,網址是www.rob-howard.net。請瞭解一下 Blackbelt TechEd 2004 示範中的投影片和示範。
技巧 7 — 頁輸出緩衝和Proxy 伺服器
ASP.NET 是您的展示層(或說應該是您的展示層);他由頁、使用者控制項、伺服器控制項(HttpHandlers 和 HttpModules)連同他們產生的內容組成。假如您具備一個 ASP.NET 頁,他會產生輸出(HTML、XML、映像或任何其他資料),並且您針對每個請求運行此代碼時,他都會產生相同的輸出,那麼您就擁有一個可用於頁輸出緩衝的絕佳備選內容。
通過將下面這行內容添加頁的最上端:
<%@ Page OutputCache VaryByParams="none" Duration="60" %>
您就能夠高效地為此頁產生一次輸出,然後對他進行多次重用,時間最長為 60 秒,此時該頁將重新執行,輸出也將再一次添加到 ASP.NET 緩衝。通過使用一些低層級可程式化API 也能夠完成此行為。對於輸出緩衝有幾個可配置的配置,如剛剛講到的 VaryByParams 屬性。VaryByParams 剛好被請求到,但還允許您指定 HTTP GET 或 HTTP POST 參數來更改快取項目。例如,只需配置 VaryByParam="Report" 即可對 default.aspx?Report=1 或 default.aspx?Report=2 進行輸出緩衝。通過指定一個以分號分隔的列表,還能夠指定其他參數。
很多人還沒有意識到當使用了輸出緩衝之後,ASP.NET 頁也會產生一些向下流到快取服務器的 HTTP 標題頭,如 Microsoft Internet Security 和 Acceleration Server 或 Akamai 使用的標題頭。配置了 HTTP 緩衝表題頭之後,能夠在這些網路資源上對文檔進行緩衝,用戶端請求也可在不必返回原始伺服器的情況下得以滿足。
因此,使用頁輸出緩衝不會使得您的應用程式效率更高,但是他可能會減少伺服器上的負載,因為下行流緩衝技術會緩衝文檔。當然,這隻能是匿名內容;一旦他成為下行流之後,您就再也不會看到這些請求,並且再也無法執行身分識別驗證以阻止對他的訪問了。
技巧 8 — 運行 IIS 6.0(哪怕只為了使用核心緩衝也好)
假如您未運行 IIS 6.0 (Windows Server 2003),那麼您就錯過了 Microsoft Web 服務器中的一些很好的效能增強。在技巧 7 中,我討論了輸出緩衝。在 IIS 5.0 中,請求是通過 IIS 然後進入 ASP.NET 的。涉及到緩衝時,ASP.NET 中的 HttpModule 會接收該請求,並返回緩衝中的內容。
假如您正在使用 IIS 6.0,就會發現一個很好的小功能,稱為核心緩衝,他無需對 ASP.NET 進行任何代碼更改。當請求由 ASP.NET 進行輸出緩衝時,IIS 核心緩衝會接收快取資料的一個副本。當請求來自網路驅動程式時,核心層級的驅動程式(無上下文轉換到使用者模式)就會接收該請求,假如經過了緩衝,則會將緩衝的資料重新整理到響應,然後完成執行。這就表示,當您將核心模式緩衝和 IIS 和 ASP.NET 輸出緩衝一起使用時,就會看到令人不敢相信的效能結果。在 ASP.NET 的 Visual Studio 2005 研發過程中,我一度是負責 ASP.NET 效能的研發經理。研發人員完成具體工作,但是我要看到每天進行的任何報告。核心模式緩衝結果總是最有意思的。最常見的特徵是網路充滿了請求/響應,而 IIS 運行時的 CPU 使用率只有大約 5%。這太令人震驚了!當然使用 IIS 6.0 更有一些其他原因,但是核心模式緩衝是其中最明顯的一個。
技巧 9 — 使用 Gzip 壓縮
雖然使用 gzip 並不一定是伺服器效能技巧(因為您可能會看到 CPU 使用率的提高),但是使用 gzip 壓縮能夠減少伺服器發送的位元組數量。這就使人們覺得頁速度加快了,並且還減少了頻寬的用量。根據所發送資料、能夠壓縮的程度連同用戶端瀏覽器是否支援(IIS 只會向支援 gzip 壓縮的用戶端發送經過 gzip 壓縮的內容,如 Internet Explorer 6.0 和 Firefox),您的伺服器每秒能夠服務於更多的請求。實際上,幾乎每當您減少所返回資料的數量時,都會增加每秒請求數。
Gzip 壓縮已內建到 IIS 6.0 中,並且其效能比 IIS 5.0 中使用的 gzip 壓縮要好的多,這是好訊息。但不幸的是,當嘗試在 IIS 6.0 中開啟 gzip 壓縮時,您可能無法在 IIS 的屬性對話中找到該配置。IIS 小組在該伺服器中置入了卓越的 gzip 功能,但是忘了包括一個用於啟用該功能的管理 UI。要啟用 gzip 壓縮,您必須深入到 IIS 6.0 的 XML 配置配置內部(這樣不會引起心臟虛弱)。順便提一句,這歸功於 OrcsWeb 的 Scott Forsyth,他協助我提出了在 OrcsWeb 上宿主的 www.asp.net 伺服器的這個問題。
本文就不講述步驟了,請閱讀 Brad Wilson 的文章,網址是 IIS6 Compression。更有一篇有關為 ASPX 啟用壓縮的知識庫文章,網址是 Enable ASPX Compression in IIS。但是您應該注意,由於一些實施細節,IIS 6.0 中不能同時存在動態壓縮和核心緩衝。
技巧 10 — 伺服器控制項檢視狀態
檢視狀態是個有趣的名稱,用於表示在所產生頁的隱藏輸出欄位中儲存一些狀態資料的 ASP.NET。當該頁發回伺服器時,伺服器能夠分析、驗證、並將此檢視狀態資料應用回該頁的控制項樹。檢視狀態是個很強大的功能,因為他允許狀態和用戶端一起保持,並且他無需 cookie 或伺服器記憶體即可儲存此狀態。很多 ASP.NET 伺服器控制項都使用檢視狀態來保持在和頁元素進行互動期間建立的配置,例如儲存對資料進行分頁時顯示的當前頁。
然而使用檢視狀態也有一些缺點。首先,當頁被服務或被請求時,他都會增加頁的總負載。對發回伺服器的檢視狀態資料進行序列化或取消序列化時,也會發生額外的開銷。最後,檢視狀態會增加伺服器上的記憶體配置。
幾個伺服器控制項有過度使用檢視狀態的趨勢,即使在並無需的情況下也要使用他,其中最著名的是 DataGrid。ViewState 屬性的預設行為是啟用,但是假如您無需,則能夠在控制項或頁層級關閉。在控制項內,只需將 EnableViewState 屬性配置為 false,或在頁中使用下列配置即可對其進行全域配置:
<%@ Page EnableViewState="false" %>
假如您不發回頁,或總是針對每個請求重建頁上的控制項,則應該在頁層級禁用檢視狀態。
小結
我為您講述了一些我認為在編寫高效能 ASP.NET 應用程式時有所協助的技巧。正如我在本文前面部分提到的那樣,這是個初步指南,並不是 ASP.NET 效能的最後定論。(有關改善 ASP.NET 應用程式效能的資訊,請參閱 Improving ASP.NET Performance。)只有通過自己的親身體驗才能找出解決具體效能問題的最好方法。但是,在您的旅程中,這些技巧應該會為您提供一些好的指南。在軟體研發中,幾乎沒有絕對的東西;每個應用程式都是唯一的。
請參閱提要欄“Common Performance Myths”。
Rob Howard 是 Telligent Systems 的創始人,專門從事高效能 Web 應用程式程式、知識庫管理員和協作系統方面的工作。Rob 以前受雇於 Microsoft,他在那裡協助設計了 ASP.NET 1.0、1.1 和 2.0 的基礎結構。要聯絡 Rob,請訪問 rhoward@telligentsystems.com。
原文連結:http://msdn.microsoft.com/msdnmag/issues/05/01/ASPNETPerformance/default.aspx