走进异步世界:博客程序的异步化改造以及发布后的不理想情况
最近,我們干了一件“驚天動(dòng)地”的事——對改了十年、代碼混亂無比、WebForms與MVC混血、ADO.NET與Entity Framework混合的博客程序,用.NET 4.5的async/await特性進(jìn)行了異步化改造。主要的異步化改造已于昨天完成,并在昨天晚上發(fā)布了異步化改造后的博客程序。
觸動(dòng)我們進(jìn)行這次異步化改造的是ASP.NET官網(wǎng)上一篇文章(Using Asynchronous Methods in ASP.NET 4.5)中的一段話:
?A web application? using synchronous methods to service high latency calls where the thread pool grows to the .NET 4.5 default maximum? of 5, 000 threads would consume approximately 5 GB more memory than an application able the service the same requests using asynchronous methods and only 50 threads.
在高延遲操作場景下,同步方式需要5000個(gè)線程才能完成的工作,采用異步方式只需50個(gè)線程!以一敵百,如此的高效,怎能不讓人心動(dòng)。
而itworld一篇文章中的一句話更是火上澆油,讓我們下定決心實(shí)現(xiàn)異步化。
I’ve seen?load tests show 300% improvement?in response times and concurrent connections boost almost 8x over the synchronous counterparts.
此次異步化改造一共有6個(gè)部分,其中三個(gè)部分的改造最輕松,它們是MVC,EF,WCF;而另外三個(gè)則最艱苦,它們是WebForms,ADO.NET,EnyimMemcached(memcached .NET客戶端)。
下面分別簡單介紹一下這6個(gè)部分的改造:
1. MVC的異步化改造
無比輕松,只要把ActionResult改為async Task<AstionResult>:
public async Task<ActionResult> SiteHome(int? pageIndex) {//... }2. Entity Framework的異步化
也很輕松,查詢時(shí)只需使用異步LINQ:
public async Task<int> GetAsync() {return await Entities.Where(...).Select(...).CountAsync(); }保存時(shí)只需SaveChangesAsync():
async Task IUnitOfWork.CommitAsync() {await base.SaveChangesAsync(); }3. WCF客戶端的異步化
照樣輕松,只要選擇“Generate task-based operations”重新生成WCF客戶端代理:
4. WebForms的異步化
a) 所有實(shí)現(xiàn)異步的.aspx都要加上async="true"標(biāo)記。
<%@ Page Async="true" Language="c#"%>b) 原來獲取數(shù)據(jù)進(jìn)行綁定的代碼要放在異步方法中,并通過Page.RegisterAsyncTask進(jìn)行注冊。
protected override void OnLoad(EventArgs e) {base.OnLoad(e);this.Page.RegisterAsyncTask(new System.Web.UI.PageAsyncTask(GetPostsByMonth)); }c) 原來靜態(tài)綁定的用戶控件不得不改為動(dòng)態(tài)加載。
同步時(shí)代:
<%@ Register TagPrefix="uc1" TagName="EntryList" Src="EntryList.ascx" %> <uc1:EntryList id="Days" DescriptionOnly = "true" runat="server"></uc1:EntryList>異步時(shí)代:
public class ArchiveMonth : UserControl {protected override void OnLoad(EventArgs e){base.OnLoad(e);this.Page.RegisterAsyncTask(new System.Web.UI.PageAsyncTask(GetPostsByMonth));}private async Task GetPostsByMonth(){ var DaysControl = LoadControl("EntryList.ascx") as EntryList;if (DaysControl != null){DaysControl.EntryListItems = await postSevice.GetEntriesByMonth(CurrentBlog, dt, PostType.BlogPost);DaysControl.DescriptionOnly = true;Controls.Add(DaysControl);}} }d) 原來在OnPreRender中的處理代碼(依賴異步任務(wù)的處理結(jié)果)需要移至Render,因?yàn)锳SP.NET是在OnPreRender階段檢查所有注冊的異步任務(wù)并進(jìn)行異步執(zhí)行。
【W(wǎng)ebFoms中的異步原理】
如果在.aspx中設(shè)置了async="true",ASP.NET線程在處理針對這個(gè)頁面的請求時(shí),會(huì)在PreRender階段查找是否有注冊的異步任務(wù)(async task);如果有,該線程會(huì)將當(dāng)前請求放回隊(duì)列中,然后抽身去處理其它請求。當(dāng)異步任務(wù)完成時(shí),該請求會(huì)被線程池中的某個(gè)線程撿起,直到執(zhí)行完成。(參考自Async Pages part 2: How to use asynchrony in your Pages)。
5.?ADO.NET的異步化
所有進(jìn)行異步化的數(shù)據(jù)庫操作都需要用類似下面的ADO.NET代碼進(jìn)行改造
using(var conn = new SqlConnection(connectionString)) {using(var command = conn.CreateCommand()){command.CommandType = CommandType.StoredProcedure;command.CommandText = "...";command.Parameters.AddWithValue("...", ...);await conn.OpenAsync();using (IDataReader reader = await command.ExecuteReaderAsync()){//... }} }6. EnyimMemcached的異步化
也就是Socket的異步化,參考msdn博客中的博文Awaiting Socket Operations,修改了EnyimMemcached,實(shí)現(xiàn)了Memcached客戶端的異步化,修改后的代碼已發(fā)布至github(https://github.com/cnblogs/EnyimMemcached)。
public async Task<IGetOperationResult<T>> GetAsync<T>(string key) {//... var commandResult = await node.ExecuteAsync(command);//... }【發(fā)布后的不理想情況】
1. CPU出現(xiàn)抖動(dòng)
異步化改造后的博客程序發(fā)布后,在阿里云云服務(wù)器上CPU出現(xiàn)抖動(dòng),后來發(fā)展為瘋狂抖動(dòng)。
最后放棄使用異步化的EnyimMemcached,改回原來同步的EnyimMemcached,CPU抖動(dòng)情況得到了改善(后來發(fā)現(xiàn)異步化后的EnyimMemcached存在內(nèi)存泄漏問題)。
a) 訪問低峰時(shí)的CPU抖動(dòng)情況
b)訪問高峰時(shí)的CPU抖動(dòng)情況
2. w3wp進(jìn)程消耗的線程與內(nèi)存更多
這個(gè)地方的表現(xiàn)讓人大跌眼鏡,原以為線程與內(nèi)存的消耗會(huì)明顯降低,實(shí)際卻不但不降反而上升。
【更新1】
我們在負(fù)載均衡中加了另外一臺云服務(wù)器,不理想情況竟然沒出現(xiàn)。
后來,我們將原先2臺表現(xiàn)不理想的服務(wù)器中的w3wp進(jìn)程重啟后,不理想情況也消失了。昨天我們發(fā)布時(shí)只是更新了dll,并沒有對w3wp進(jìn)程進(jìn)行回收。
【更新2】
重啟w3wp進(jìn)程之后,還是會(huì)出現(xiàn)CPU抖動(dòng)的情況,但目前觀測下來對響應(yīng)速度未造成影響。我們猜測CPU抖動(dòng)可能與并行處理有關(guān)。
【更新3】
解決進(jìn)展:
1. 發(fā)現(xiàn)一個(gè)異步方法中調(diào)用了System.Web.HttpContext.Current,去掉了這個(gè)調(diào)用。
2. 增加ConfigureAwait(false)的使用。
【參考資料】
Best Practices in Asynchronous Programming
Using Asynchronous Methods in ASP.NET 4.5
Async Pages part 2: How to use asynchrony in your Pages
How to create Asynchronous device Page in ASP.NET 4.5
Why you should use async tasks in .NET 4.5 and Entity Framework 6?
Awaiting Socket Operations
轉(zhuǎn)載于:https://www.cnblogs.com/cmt/p/aspnet_async_await.html
總結(jié)
以上是生活随笔為你收集整理的走进异步世界:博客程序的异步化改造以及发布后的不理想情况的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu 的远程桌面
- 下一篇: MetInfo安装