走进异步世界:博客程序的异步化改造以及发布后的不理想情况
最近,我們干了一件“驚天動地”的事——對改了十年、代碼混亂無比、WebForms與MVC混血、ADO.NET與Entity Framework混合的博客程序,用.NET 4.5的async/await特性進行了異步化改造。主要的異步化改造已于昨天完成,并在昨天晚上發布了異步化改造后的博客程序。
觸動我們進行這次異步化改造的是ASP.NET官網上一篇文章(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個線程才能完成的工作,采用異步方式只需50個線程!以一敵百,如此的高效,怎能不讓人心動。
而itworld一篇文章中的一句話更是火上澆油,讓我們下定決心實現異步化。
I’ve seen?load tests show 300% improvement?in response times and concurrent connections boost almost 8x over the synchronous counterparts.
此次異步化改造一共有6個部分,其中三個部分的改造最輕松,它們是MVC,EF,WCF;而另外三個則最艱苦,它們是WebForms,ADO.NET,EnyimMemcached(memcached .NET客戶端)。
下面分別簡單介紹一下這6個部分的改造:
1. MVC的異步化改造
無比輕松,只要把ActionResult改為async Task<AstionResult>:
public async Task<ActionResult> SiteHome(int? pageIndex) {//... }2. Entity Framework的異步化
也很輕松,查詢時只需使用異步LINQ:
public async Task<int> GetAsync() {return await Entities.Where(...).Select(...).CountAsync(); }保存時只需SaveChangesAsync():
async Task IUnitOfWork.CommitAsync() {await base.SaveChangesAsync(); }3. WCF客戶端的異步化
照樣輕松,只要選擇“Generate task-based operations”重新生成WCF客戶端代理:
4. WebForms的異步化
a) 所有實現異步的.aspx都要加上async="true"標記。
<%@ Page Async="true" Language="c#"%>b) 原來獲取數據進行綁定的代碼要放在異步方法中,并通過Page.RegisterAsyncTask進行注冊。
protected override void OnLoad(EventArgs e) {base.OnLoad(e);this.Page.RegisterAsyncTask(new System.Web.UI.PageAsyncTask(GetPostsByMonth)); }c) 原來靜態綁定的用戶控件不得不改為動態加載。
同步時代:
<%@ Register TagPrefix="uc1" TagName="EntryList" Src="EntryList.ascx" %> <uc1:EntryList id="Days" DescriptionOnly = "true" runat="server"></uc1:EntryList>異步時代:
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中的處理代碼(依賴異步任務的處理結果)需要移至Render,因為ASP.NET是在OnPreRender階段檢查所有注冊的異步任務并進行異步執行。
【WebFoms中的異步原理】
如果在.aspx中設置了async="true",ASP.NET線程在處理針對這個頁面的請求時,會在PreRender階段查找是否有注冊的異步任務(async task);如果有,該線程會將當前請求放回隊列中,然后抽身去處理其它請求。當異步任務完成時,該請求會被線程池中的某個線程撿起,直到執行完成。(參考自Async Pages part 2: How to use asynchrony in your Pages)。
5.?ADO.NET的異步化
所有進行異步化的數據庫操作都需要用類似下面的ADO.NET代碼進行改造
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,實現了Memcached客戶端的異步化,修改后的代碼已發布至github(https://github.com/cnblogs/EnyimMemcached)。
public async Task<IGetOperationResult<T>> GetAsync<T>(string key) {//... var commandResult = await node.ExecuteAsync(command);//... }【發布后的不理想情況】
1. CPU出現抖動
異步化改造后的博客程序發布后,在阿里云云服務器上CPU出現抖動,后來發展為瘋狂抖動。
最后放棄使用異步化的EnyimMemcached,改回原來同步的EnyimMemcached,CPU抖動情況得到了改善(后來發現異步化后的EnyimMemcached存在內存泄漏問題)。
a) 訪問低峰時的CPU抖動情況
b)訪問高峰時的CPU抖動情況
2. w3wp進程消耗的線程與內存更多
這個地方的表現讓人大跌眼鏡,原以為線程與內存的消耗會明顯降低,實際卻不但不降反而上升。
【更新1】
我們在負載均衡中加了另外一臺云服務器,不理想情況竟然沒出現。
后來,我們將原先2臺表現不理想的服務器中的w3wp進程重啟后,不理想情況也消失了。昨天我們發布時只是更新了dll,并沒有對w3wp進程進行回收。
【更新2】
重啟w3wp進程之后,還是會出現CPU抖動的情況,但目前觀測下來對響應速度未造成影響。我們猜測CPU抖動可能與并行處理有關。
【更新3】
解決進展:
1. 發現一個異步方法中調用了System.Web.HttpContext.Current,去掉了這個調用。
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
轉載于:https://www.cnblogs.com/cmt/p/aspnet_async_await.html
總結
以上是生活随笔為你收集整理的走进异步世界:博客程序的异步化改造以及发布后的不理想情况的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu 的远程桌面
- 下一篇: MetInfo安装