久久人人97超碰超碰窝窝_国产精品久久久久久搜索_AV在线网站无码不卡的_亚洲AV永久精品爱情岛论坛

rexian

咨詢電話:023-6276-4481

熱門文章

聯(lián)系方式

電 話:023-6276-4481

郵箱:broiling@qq.com

地址:重慶市南岸區(qū)亞太商谷6幢25-2

當前位置:網(wǎng)站首頁 > 技術文章 > 【給中高級開發(fā)者】構建高性能ASP.NET應用的幾點建議

【給中高級開發(fā)者】構建高性能ASP.NET應用的幾點建議

編輯:T.T 發(fā)表時間:2017-09-05 09:49:19
T.T

如果你在構建一個面向公眾的web站點,那么在項目結尾時你想要實現(xiàn)的就是web負載性能良好。這意味著,你要確保你的產(chǎn)品在高負載下(50個并發(fā)用戶或者每秒200個用戶等等)能夠運行,即使你認為此時不會有那么大的負載。久而久之,你的web站點可能吸引越來越多的用戶,此時如果web的負載難以讓人忍受時,那么自然而然網(wǎng)站開始走下坡路,意味著客戶流失以及名譽受損。

那么可以采取哪些措施可以使得一個ASP.NET或者ASP.NET MVC應用更加高性能呢?

早期階段就要對應用進行負載測試

大多數(shù)開發(fā)者趨向在應用開發(fā)完成后,集成測試和回歸測試通過后才進行負載測試。盡管在開發(fā)完成后執(zhí)行一次負載測試好過不做,但是一旦完成了代碼的編寫,修復性能問題就為時已晚了。這個問題最常見的例子就是當應用程序在負載測試時不能正確響應時,就會考慮向外擴展(增加更多的服務器)。有時這是不可能的,因為代碼不適合實現(xiàn)擴展服務器。比如當對象是存儲在Session中并不可序列化時,這樣添加更多的web節(jié)點或者工作者進程就是不可能的。如果你在開發(fā)的早期階段就發(fā)現(xiàn)你的應用可能要部署到多臺服務器上,那么你應該在配置和服務器的數(shù)量等方面都要讓測試環(huán)境和最終環(huán)境盡可能地接近,這樣你的代碼會更容易適應。

使用高性能類庫

近來我在診斷web站點的性能問題時發(fā)現(xiàn)了代碼中的一個熱點問題:來自第三方web服務的JSON信息必須要被反序列化多次。那些Json信息是由Newtonsoft.Json反序列化的,并且證明了Newtonsoft.Json在反序列化時不是最快的類庫,然后我們使用了一個更快的類庫(如ServiceStack)替代了Json.Net,并獲得了更好的結果。

如果我們在挑選了Json.Net作為序列化類庫的早期階段就完成了負載測試,那么我們就會更早地發(fā)現(xiàn)性能問題,我們就不必再代碼中做這么多的修改了,也不必再次完整地重新測試了。

你的應用是CPU密集還是IO密集的?

在開始實現(xiàn)web應用以及設計項目時,你要首先考慮的一件事就是你的應用是CPU密集的還是IO密集的?這對于了解擴展應用的策略是很重要的。

比如,如果你的應用是CPU密集的,那么你可能想使用同步模式,并行處理等等。然而,對于一個有很多IO受限的操作(例如和外部web服務或者網(wǎng)絡資源如數(shù)據(jù)庫通訊)的產(chǎn)品,基于Task的異步模式可能對于向外擴展更有幫助。此外,為了在未來的某天能夠創(chuàng)建Web Gardens和Web Farms,你可能想有一個集中式的緩存系統(tǒng),這樣就實現(xiàn)了跨越多個工作者進程或服務的負載。

使用基于Task的異步模型,但要慎重

如果你的產(chǎn)品依賴許多IO受限的操作,或者包括了可能需要消耗寶貴的IIS線程等待一個操作完成的長時間運行的操作,你最好為ASP.NET MVC項目考慮使用基于Task的異步模式。

互聯(lián)網(wǎng)上有很多關于異步的ASP.NET MVC actions的手冊(比如這個),所以這篇博客盡量避免介紹關于異步的知識點。然而,必須指出ASP.NET (MVC)中傳統(tǒng)的同步action會造成IIS線程繁忙直到操作完成或請求處理完成。這意味著如果如果web應用在等待一個外部的資源(如一個web服務)響應,那么該線程將是繁忙的。.Net線程池中可以用于處理請求的線程數(shù)量也是有限的,因此,盡可能快地釋放線程是很重要的?;赥ask的異步action或方法會在請求處理后釋放該線程,然后從線程池中獲取新的線程,并使用該新的線程返回該action的結果。這樣,多個請求可以由多個線程處理,這會為你的應用帶來更好的響應。

雖然TAP模式對于適當?shù)膽脕碚f很方便,但必須要慎重使用。當你使用TAP設計或者實現(xiàn)一個項目時,必須要注意幾個點(你可以點擊這里查看),然而,開發(fā)者使用async和await關鍵字面臨的最大挑戰(zhàn)是知道在這個上下文中他們必須略有不同地處理線程。比如,可以創(chuàng)建一個返回Task(如Task《Product》,博客園的markdown不支持單尖括號)的方法,通常你可以對那個Task調用Run()方法或者只調用task.Result來迫使運行該task,直到拿到結果。

分發(fā)緩存和會話(session)狀態(tài)

開發(fā)者在單個開發(fā)機器上構建一個web應用是非常常見的,并且也會假設該產(chǎn)品運行在單個服務器上,然而對于面向大眾的web通常不是這樣的。它們往往被部署到一個叫做負載均衡之后的多個服務器上。盡管你仍然可以使用In-Proc(關于In-Proc和Out-Proc的知識點補充)的模式和粘性(sticky)Session將一個web應用部署到多個服務器上(負載均衡器會將屬于相同session的所有請求定向到單個服務器),你可能必須保留session數(shù)據(jù)和緩存數(shù)據(jù)的多個副本。比如,如果你將產(chǎn)品部署到由4臺服務器組成的web farm上,并且你保持session數(shù)據(jù)為In-Proc,那時一個請求到達一個已經(jīng)包含緩存數(shù)據(jù)的機會是四分之一或25%,然而你如果在正確的地方使用了集中式緩存機制,那么為每個請求找到緩存條目的機會就是100%。這對于嚴重依賴緩存數(shù)據(jù)的web應用是至關重要的。

使用集中式緩存機制(像App Fabric或Redis)的另一個好處是對實際的產(chǎn)品實現(xiàn)主動緩存系統(tǒng)的能力。主動緩存機制可以用于在客戶端請求一個條目之前就可以將最受歡迎的條目預加載到緩存中。如果你使用實際的數(shù)據(jù)源來管理緩存同步時,那么這會幫助你大幅地改善大數(shù)據(jù)驅動應用的性能。

創(chuàng)建Web Gardens

之前提到,在一個IO受限的web應用中包含了很多長時間運行的操作(如web服務的調用),此時你可能想盡可能地釋放主線程。默認情況下,每個web應用運行在一個主線程下,該主線程為保持web站點存活(alive)負責,但不幸的是,當它太忙的時候,你的站點就變得不能響應了。給應用添加更多的“主線程”是一種辦法,這是通過將更多的工作者進程添加到IIS下的Web站點來實現(xiàn)的。每個工作者進程都會包括一個單獨的主線程,因此,如果一個主線程繁忙,還有另外的主線程來處理到來的請求。

擁有多個工作者進程會將你的web站點變成一個Web Garden,這要求你將Session和應用數(shù)據(jù)持久化到Out-Proc中(例如一個state server或者Sql Server)。

巧妙地使用緩存和懶加載

無需強調,如果你將經(jīng)常訪問的一點數(shù)據(jù)緩存到內存中,那么這會減少數(shù)據(jù)庫和web服務的調用。正如之前所說,這對于IO受限的應用特別有幫助,當網(wǎng)站處于低負載時可能會造成不幸。

提高站點響應的另一種方式是使用懶加載。懶加載意味著應用里不會有確定的數(shù)據(jù)片,但是它知道數(shù)據(jù)在哪里。比如,如果web頁面上有一個下拉列表,這意味著要顯示一個產(chǎn)品列表,一旦頁面加載完畢,不必從數(shù)據(jù)庫中加載所有的產(chǎn)品。你可以在頁面中添加一個jQuery函數(shù),該函數(shù)可以在第一次下拉時填充下拉列表。你也可以在代碼中的許多地方使用相同的技術,比如當使用Linq查詢和CLR集合時。

不要在MVC視圖中放C#代碼

ASP.NET MVC視圖會在運行時編譯而不是在編譯時,因此,如果在視圖中包含了太多的C#代碼,那么你的代碼不會編譯后放到dll文件中。這樣做不僅有害于軟件的可測試性,還會使web應用更慢,因為每個頁面都有花更長的時間獲得顯示(因為它們必須被編譯)。將代碼添加到視圖中的其他缺陷在于它們不能異步運行,這樣,如果你決定基于TAP來構建應用時,就不能充分利用視圖中的異步方法和action了。

比如,如果你的代碼中有這么一個方法:

public async Task<string> GetName(int code){    var result = …    return await result;

}

該方法可以在一個異步的ASP.NET MVC action的上下文中異步運行,比如:

public Task<ActionResult> Index( CancellationToken ctx ){    var name = await GetName( 100 );
}

但是如果在一個視圖中調用這方法,因為該視圖不是異步的,所以必須以一種線程阻塞(thread-blocking)的方式運行,如:

var name = GetName(100).Result;

.Result會阻塞運行的線程,直到GetName()處理完成了請求,這樣該應用的執(zhí)行會停止一會兒,然而當使用await關鍵字調用該代碼時,該線程不會被阻塞。

適當時使用Fire & Forget

注:Fire & Forget,字面意思是發(fā)射,然后忘記,不去理會了。網(wǎng)絡釋義為射后不理。
如果兩個或多個操作沒有生成單個事務,那么你很可能不必按順序運行它們。比如,如果一個用戶在你的站點注冊時創(chuàng)建了一個賬戶,一旦他們注冊了,你就把他們的詳細信息保存到數(shù)據(jù)庫,然后給他們發(fā)送一封郵件,你不必等待郵件發(fā)送出去才結束這個操作。

在這種情況下,最好的方法很可能就是開啟一個新的線程,讓它給用戶發(fā)送郵件,然后返回到主線程。這就叫做Fire& Forget,它可以提高應用的響應能力。

為x64 CPU創(chuàng)建

32-bit的應用局限于較低的內存量和可以訪問更少的CPU功能/指令。要克服這些限制,如果你的服務器是64-bit的,那么要確保你的應用運行在64-bit模式下(通過確保在IIS的32-bit模式下運行網(wǎng)站的選項是不是開啟)。然后為x64 CPU編譯并生成代碼而不是Any CPU。

x64有用的一個例子是提高數(shù)據(jù)驅動應用的響應能力和性能,有一個好的緩存機制是必須的。In-proc是消耗內存的,因為一切都存儲在網(wǎng)站的應用程序池的內存邊界。對于x86進程來說,可以分配的內存量限制到4GB,這樣,如果加載到緩存中,這個限制很快就被打破。如果相同的站點是為x64 CPU顯式生成的,那么內存限制就不需考慮了,這樣更多的條目可以加到緩存中,然后和數(shù)據(jù)庫的通訊就少了,這會帶來更好的性能。

使用服務器上的監(jiān)視和診斷工具

可能存在你肉眼看不到的更多的性能問題,因為它們不會出現(xiàn)在錯誤日志中。當應用程序已經(jīng)部署到基本沒有機會調試的服務器上時,識別性能問題是更嚇人的事情。

要找出緩慢的處理,線程阻塞,懸掛,錯誤等等,強烈建議在服務器上安裝一個監(jiān)視和診斷工具,讓它們持續(xù)地跟蹤和監(jiān)視你的應用程序。我個人使用的是NewRelic檢查我的在線網(wǎng)站的健康。獲取詳細信息并創(chuàng)建免費賬號看這里哦!

分析運行中的應用

一旦完成了網(wǎng)站開發(fā),部署到IIS,然后附加一個分析器(如Visual Studio Profiler),然后抓取應用的多個部分的快照。比如抓取購買或者用戶注冊等操作的快照,然后檢查病查看是否存在任何造成緩慢或阻塞的代碼。在早期階段找到那些熱點可能會節(jié)省你大量的時間,榮譽和金錢。