如何开发ASP.NET MVC异步式(Async)处理应用
2012-10-07 14:17
701 查看
这个假期抽了点时间,在家准备一个项目的技术方案。特地对ASP.NET MVC框架下开发异步模式(ASync)应用做了一番研究。
这个项目的目标是开发出一个WEB应用,该应用的并发业务请求量非常大,而业务的处理,则大部分由第三方系统提供的Web Service完成。
为了提高系统的吞吐量,采用异步式模型来调用第三方的Web Service是最好的选择。在此,先简单介绍一下异步式模型的好处。
IIS内有一个工作线程池,处理每个HTTP请求时,都会由工作线程池的一个线程进行处理,当该线程在执行对外部资源的请求时,比如说,数据库请求、Web Service,该线程就会堵塞,等到外部资源请求返回时,才会继续执行。由于工作线程的数量有限,当HTTP请求的进入数目超过工作线程可以处理的速度时,IIS就会将请求放进请求队列里,当队列满了以后,新的请求就会出现503错误。
如果采用异步式模型,则外部资源的请求可以交由IIS外部的另一个线程池的线程负责,IIS的工作线程不会堵塞,而是立即返回,继续处理下一个请求,当外部线程将外部资源返回的结果获得后,再唤醒IIS工作线程池的空闲线程,完成后续的代码处理,并将结果返回给请求发出者。
由此可见,采用异步式模型最大的好处是,将耗时的外部资源请求操作与IIS工作线程的工作并行处理了,提升了IIS工作线程的处理效率,缩短了请求的等待时间,从而提升了IIS的吞吐量。
要实现WEB应用的异步式处理,也很简单。大致可以分以下几步:
根据MSDN的介绍,实现一个异步式调用的方法有三种设计模式:
异步编程模型(Asynchronous Program Model),简称APM。
基于事件的异步模型(Event-based Asynchronous Pattern),简称EAP。
基于任务的异步模型(Task-based Asnychronous Pattern),简称TAP。
其中,以APM实现最简单。
首先,定义一个delegate,该delegate的签名跟同步方法一样。
.NET对delegate有一个内置的异步式调用支持,任何delegate都缺省有BeginInvoke和EndInvoke两个调用方法。请看以下举例。
一个同步方法的定义如下:
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
// Create a class that simulate long time consuming task.
public class NumberFinder
{
public bool Find(
int number,
out int index)
{
//takes long time to run
Thread.Sleep(1000);
if (number>0 )
{
index=1;
return true;
}
else
{
index=0;
return false ;
}
}
}
为了实现NumberFinder.Find方法的异步式调用,定义以下的委托:
AsyncFindCaller自动就会有以下两个支持异步式调用的方法:
1. IAsyncResult BeginInvoke(int number, out int index, AsyncCallback callback,Object userstate);
调用者线程调用该方法后,系统工作线程池的线程就会执行该委托对应的同步方法,当该同步方法执行完毕后,异步工作线程就会调用第三个参数代表的回调方法(如果该参数不为null的话),一般用该回调方法来通知调用者,已经完成了方法的执行。
第四个参数用来传递调用者的上下文参数,以便在异步式执行完毕后,能重新获得这些上下文参数,继续执行后续的代码。
2. bool EndInvoke(out int index, IAsyncResult ar);
该方法的返回值就是原同步方法的返回值,参数的签名包括了ref,out 参数。调用BeginInvoke完成了同步方法的执行后,需要再调用这个方法,真正获得返回的结果,如果同步方法在执行过程中出现了异常,那么调用该方法时,才会产生异常。
下面演示如何将NumberFinder.Find同步方法变成异步式调用,注意,这仅仅是一个示意代码,并不是完整的程序,只是为了讲解异步式调用的大致开发思路:
以下的代码演示如何执行异步式调用
上述步骤演示了如何实现一个异步式方法以及异步式调用的过程。
当有了一个可以支持异步调用的类之后,需要做的第二步就是在ASP.NET MVC架构中实现WEB异步式调用。
有几个地方需要注意的:
控制器必须继承自AsyncController;
如果Action名称是Index,则需要有两个public method需要定义:
1. IndexAsync:
可接收 Model Binding过来的值
回传类别必须为void
4000
可套用Action Filter,比如超时值
调用AsyncManager.OutstandingOperations.increment加1增加内部异步计数器,如果有多个异步工作要执行,那么就要加多个
异步调用委托,该委托的回调方法参数 要传入回调方法,使其能调用委托的EndInvoke方法获得返回值,而且可以通知MVC异步工作已经完成
2. IndexCompleted:
当AsyncManager.OutstandingOperations归零时,IIS就会从工作线程池里调度线程,执行IndexCompleted 方法
该方法返回值可以是 void、null、ActionResult 或其他 object,与原本的 ASP.NET MVC 返回的规则是一样的。
传入的参数名称可以用AsyncManager.Parameters设定的key名称来当参数名称,这些参数值会自动被传入
这两个方法之间的桥梁就是CallbackMethod,异步工作线程调用委托代表的同步方法完成后,调用这个回调方法,在这个回调方法里,调用EndInvoke获得返回值,并通过AsyncManager.Parameters来传递参数给IndexCompleted。
其实这是很简单的过程,只是好像网上没有文章将这些步骤串起来,所以成此博文,希望对读者有所帮助。
另:上述代码只是用于展示一个流程,可能会有错误,特此提醒。
学习MVC异步式编程,我参考了以下资料,如果对概念还不太明白,可以参考以下文章:
Asynchronous Programming Model (APM)
ASP.NET MVC 開發心得分享 (18):非同步控制器開發
细说ASP.NET的各种异步操作
这个项目的目标是开发出一个WEB应用,该应用的并发业务请求量非常大,而业务的处理,则大部分由第三方系统提供的Web Service完成。
为了提高系统的吞吐量,采用异步式模型来调用第三方的Web Service是最好的选择。在此,先简单介绍一下异步式模型的好处。
IIS内有一个工作线程池,处理每个HTTP请求时,都会由工作线程池的一个线程进行处理,当该线程在执行对外部资源的请求时,比如说,数据库请求、Web Service,该线程就会堵塞,等到外部资源请求返回时,才会继续执行。由于工作线程的数量有限,当HTTP请求的进入数目超过工作线程可以处理的速度时,IIS就会将请求放进请求队列里,当队列满了以后,新的请求就会出现503错误。
如果采用异步式模型,则外部资源的请求可以交由IIS外部的另一个线程池的线程负责,IIS的工作线程不会堵塞,而是立即返回,继续处理下一个请求,当外部线程将外部资源返回的结果获得后,再唤醒IIS工作线程池的空闲线程,完成后续的代码处理,并将结果返回给请求发出者。
由此可见,采用异步式模型最大的好处是,将耗时的外部资源请求操作与IIS工作线程的工作并行处理了,提升了IIS工作线程的处理效率,缩短了请求的等待时间,从而提升了IIS的吞吐量。
要实现WEB应用的异步式处理,也很简单。大致可以分以下几步:
一、将一个同步调用的方法转换为支持异步式调用
平时大家开发的类的方法,都是支持同步式调用的,如果这个方法要用在异步式调用中,则要做一些改造。根据MSDN的介绍,实现一个异步式调用的方法有三种设计模式:
异步编程模型(Asynchronous Program Model),简称APM。
基于事件的异步模型(Event-based Asynchronous Pattern),简称EAP。
基于任务的异步模型(Task-based Asnychronous Pattern),简称TAP。
其中,以APM实现最简单。
首先,定义一个delegate,该delegate的签名跟同步方法一样。
.NET对delegate有一个内置的异步式调用支持,任何delegate都缺省有BeginInvoke和EndInvoke两个调用方法。请看以下举例。
一个同步方法的定义如下:
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace Examples.AdvancedProgramming.AsynchronousOperations
{
// Create a class that simulate long time consuming task.
public class NumberFinder
{
public bool Find(
int number,
out int index)
{
//takes long time to run
Thread.Sleep(1000);
if (number>0 )
{
index=1;
return true;
}
else
{
index=0;
return false ;
}
}
}
为了实现NumberFinder.Find方法的异步式调用,定义以下的委托:
public delegate bool AsyncFindCaller ( int number, out int index);
AsyncFindCaller自动就会有以下两个支持异步式调用的方法:
1. IAsyncResult BeginInvoke(int number, out int index, AsyncCallback callback,Object userstate);
调用者线程调用该方法后,系统工作线程池的线程就会执行该委托对应的同步方法,当该同步方法执行完毕后,异步工作线程就会调用第三个参数代表的回调方法(如果该参数不为null的话),一般用该回调方法来通知调用者,已经完成了方法的执行。
第四个参数用来传递调用者的上下文参数,以便在异步式执行完毕后,能重新获得这些上下文参数,继续执行后续的代码。
2. bool EndInvoke(out int index, IAsyncResult ar);
该方法的返回值就是原同步方法的返回值,参数的签名包括了ref,out 参数。调用BeginInvoke完成了同步方法的执行后,需要再调用这个方法,真正获得返回的结果,如果同步方法在执行过程中出现了异常,那么调用该方法时,才会产生异常。
下面演示如何将NumberFinder.Find同步方法变成异步式调用,注意,这仅仅是一个示意代码,并不是完整的程序,只是为了讲解异步式调用的大致开发思路:
//定义一个回调方法,在异步方法完成返回时会被调用 void CallbackMethod(IAsyncResult ar){ AsyncResult result=ar; AsyncFinderCaller caller=(AsyncFinderCall)result.AsyncDelegate; int number=(int)result.AsyncState; int index=0; bool isFound=false; isFound=caller.EndInvoke(index,ar);//注意,在这里调用EndInvoke,以获得异步调用的方法返回的结果 Console.WriteLine("find number {0}? {1}",number,isFound); }
以下的代码演示如何执行异步式调用
public void Main(){ int number=1000; int index=0; NumberFinder numFinder=new NumberFinder(); AsyncFindCaller caller=new AsyncFindCaller(numFinder.Find); IAsyncResult ar=caller.BeginInvoke(number,index,new AsyncCallBack(CallbackMethod),number); Thread.Sleep(10000);//在这里停顿足够长的时间,模拟主线程与异步调用同步执行的效果,此时异步调用正在执行,直至CallbackMethod被执行 }
上述步骤演示了如何实现一个异步式方法以及异步式调用的过程。
当有了一个可以支持异步调用的类之后,需要做的第二步就是在ASP.NET MVC架构中实现WEB异步式调用。
二、开发ASP.NET MVC 异步控制器
众所周知,ASP.NET MVC 2.0引入了AsyncController,所以这方便了我们的程序实现。下面是一个例子,展示了一个异步执行的Action,该Action调用上述的异步委托,获得返回值。using System; using System.Web.Mvc; using System.Net; using System.IO; using Examples.AdvancedProgramming.AsynchronousOperations; namespace MvcApplication1.Controllers {
[HandleError] public class HomeController : AsyncController { [AsyncTimeout(5000)] // 设定Action的超时时间是5秒 public void IndexAsync() { AsyncManager.OutstandingOperations.Increment(1); //请注意下面的AsyncManager.OutstandingOperation.Decremenet() int number=1000; int index=0; NumberFinder numFinder=new NumberFinder(); AsyncFindCaller caller=new AsyncFindCaller(numFinder.Find); IAsyncResult ar=caller.BeginInvoke(number,index,new AsyncCallBack(CallbackMethod),number); } //定义一个回调方法,在异步方法完成返回时会被调用 void CallbackMethod(IAsyncResult ar){ AsyncResult result=ar; AsyncFinderCaller caller=(AsyncFinderCall)result.AsyncDelegate; int number=(int)result.AsyncState; int index=0; bool isFound=false; isFound=caller.EndInvoke(index,ar);//注意,在这里调用EndInvoke,以获得异步调用的方法返回的结果 AsyncManager.Parameters["Found"]=isFound;//通过AsyncManager.Parameters,向IndexComplete传参数 AsyncManager.Parameters["index"]=index; AsyncManager.OutstandingOperations.Decrement();//当OutstandingOperatioin=0时,MVC就去调用IndexCompleted了 } public ActionResult IndexCompleted(bool Found,int index) { ViewData["Found"]=Found; ViewData["index"]=index; return View(); } } }
有几个地方需要注意的:
控制器必须继承自AsyncController;
如果Action名称是Index,则需要有两个public method需要定义:
1. IndexAsync:
可接收 Model Binding过来的值
回传类别必须为void
4000
可套用Action Filter,比如超时值
调用AsyncManager.OutstandingOperations.increment加1增加内部异步计数器,如果有多个异步工作要执行,那么就要加多个
异步调用委托,该委托的回调方法参数 要传入回调方法,使其能调用委托的EndInvoke方法获得返回值,而且可以通知MVC异步工作已经完成
2. IndexCompleted:
当AsyncManager.OutstandingOperations归零时,IIS就会从工作线程池里调度线程,执行IndexCompleted 方法
该方法返回值可以是 void、null、ActionResult 或其他 object,与原本的 ASP.NET MVC 返回的规则是一样的。
传入的参数名称可以用AsyncManager.Parameters设定的key名称来当参数名称,这些参数值会自动被传入
这两个方法之间的桥梁就是CallbackMethod,异步工作线程调用委托代表的同步方法完成后,调用这个回调方法,在这个回调方法里,调用EndInvoke获得返回值,并通过AsyncManager.Parameters来传递参数给IndexCompleted。
其实这是很简单的过程,只是好像网上没有文章将这些步骤串起来,所以成此博文,希望对读者有所帮助。
另:上述代码只是用于展示一个流程,可能会有错误,特此提醒。
学习MVC异步式编程,我参考了以下资料,如果对概念还不太明白,可以参考以下文章:
Asynchronous Programming Model (APM)
ASP.NET MVC 開發心得分享 (18):非同步控制器開發
细说ASP.NET的各种异步操作
相关文章推荐
- ASP.NET MVC 中如何用自定义 Handler 来处理来自 AJAX 请求的 HttpRequestValidationException 错误
- Asp.net MVC 3实例学习之ExtShop(一)————创建应用并设置开发环境
- 【MVC 过滤器的应用】ASP.NET MVC 如何统计 Action 方法的执行时间
- Asp.net MVC 3实例学习之ExtShop(一)————创建应用并设置开发环境
- 如何应用ASP.NET MVC中的分部视图
- 高级别问题:如何在ASP.NET大型应用系统的模块化开发实现多版本程序集并存支持
- ASP.NET Core应用针对静态文件请求的处理[4]: DirectoryBrowserMiddleware中间件如何呈现目录结构
- ASP.NET MVC 的统一异常处理有多难?(衍生的意图,出错后如何保持表单的状态?)
- Asp.net MVC 3实例学习之ExtShop(一)————创建应用并设置开发环境
- ASP.NET MVC应用安全性(一)——自定义错误处理
- ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器
- Asp.net MVC 3实例学习之ExtShop(一)――――创建应用并设置开发环境
- ASP.NET MVC应用中一个诡异错误的处理
- [翻译-ASP.NET MVC]Contact Manager开发之旅之迭代2 - 修改样式,美化应用 【转】
- 使用一般处理程序,模拟ASP.NET MVC,开发一个轻量级的学习版MVC框架。
- ASP.NET Core应用的错误处理[4]:StatusCodePagesMiddleware中间件如何针对响应码呈现错误页面
- 重建程序员能力(2)-如何使asp.net mvc应用增加js和其他功能
- 如何提高码农产量,基于ASP.NET MVC的敏捷开发框架之移动端开发随笔二
- Java进击C#——应用开发之Asp.net MVC
- ASP.NET 2.0如何实现数据库应用开发