博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
走进异步世界:博客程序的异步化改造以及发布后的不理想情况
阅读量:5066 次
发布时间:2019-06-12

本文共 3914 字,大约阅读时间需要 13 分钟。

最近,我们干了一件“惊天动地”的事——对改了十年、代码混乱无比、WebForms与MVC混血、ADO.NET与Entity Framework混合的博客程序,用.NET 4.5的async/await特性进行了异步化改造。主要的异步化改造已于昨天完成,并在昨天晚上发布了异步化改造后的博客程序。

触动我们进行这次异步化改造的是ASP.NET官网上一篇文章()中的一段话:

 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
SiteHome(int? pageIndex){ //...}

2. Entity Framework的异步化

也很轻松,查询时只需使用异步LINQ:

public async Task
GetAsync(){ return await Entities .Where(...) .Select(...) .CountAsync();}

保存时只需SaveChangesAsync():

async Task IUnitOfWork.CommitAsync(){    await base.SaveChangesAsync();}

3. WCF客户端的异步化

照样轻松,只要选择“Generate task-based operations”重新生成WCF客户端代理:

WCF Generate task-based operations

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" %>

异步时代:

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);如果有,该线程会将当前请求放回队列中,然后抽身去处理其它请求。当异步任务完成时,该请求会被线程池中的某个线程捡起,直到执行完成。(参考自)。

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博客中的博文,修改了EnyimMemcached,实现了Memcached客户端的异步化,修改后的代码已发布至github()。

public async Task
> GetAsync
(string key){ //... var commandResult = await node.ExecuteAsync(command); //...}

【发布后的不理想情况】

1. CPU出现抖动

异步化改造后的博客程序发布后,在阿里云云服务器上CPU出现抖动,后来发展为疯狂抖动。

CPU抖动1

CPU抖动2

最后放弃使用异步化的EnyimMemcached,改回原来同步的EnyimMemcached,CPU抖动情况得到了改善(后来发现异步化后的EnyimMemcached存在内存泄漏问题)。

a) 访问低峰时的CPU抖动情况

访问低峰时的CPU抖动情况

b)访问高峰时的CPU抖动情况

访问高峰时的CPU抖动情况

2. w3wp进程消耗的线程与内存更多

这个地方的表现让人大跌眼镜,原以为线程与内存的消耗会明显降低,实际却不但不降反而上升。

【更新1】

我们在负载均衡中加了另外一台云服务器,不理想情况竟然没出现。

后来,我们将原先2台表现不理想的服务器中的w3wp进程重启后,不理想情况也消失了。昨天我们发布时只是更新了dll,并没有对w3wp进程进行回收。

【更新2】

重启w3wp进程之后,还是会出现CPU抖动的情况,但目前观测下来对响应速度未造成影响。我们猜测CPU抖动可能与并行处理有关。

CPU依然抖动

【更新3】

解决进展:

1. 发现一个异步方法中调用了System.Web.HttpContext.Current,去掉了这个调用。

2. 增加ConfigureAwait(false)的使用。

【参考资料】

转载于:https://www.cnblogs.com/cmt/p/aspnet_async_await.html

你可能感兴趣的文章
MySQL服务读取参数文件my.cnf的规律研究探索
查看>>
java string(转)
查看>>
__all__有趣的属性
查看>>
写博客
查看>>
利用循环播放dataurl的视频来防止锁屏:NoSleep.js
查看>>
python3 生成器与迭代器
查看>>
java编写提升性能的代码
查看>>
ios封装静态库技巧两则
查看>>
Educational Codeforces Round 46 (Rated for Div. 2)
查看>>
Abstract Factory Pattern
查看>>
C# 实现Bresenham算法(vs2010)
查看>>
基于iSCSI的SQL Server 2012群集测试(一)--SQL群集安装
查看>>
list 容器 排序函数.xml
查看>>
Activity启动过程中获取组件宽高的五种方式
查看>>
java导出Excel表格简单的方法
查看>>
SQLite数据库简介
查看>>
利用堆实现堆排序&amp;优先队列
查看>>
Mono源码学习笔记:Console类(四)
查看>>
Android学习路线(十二)Activity生命周期——启动一个Activity
查看>>
《Genesis-3D开源游戏引擎完整实例教程-跑酷游戏篇03:暂停游戏》
查看>>