您的位置:首页 > 编程语言 > ASP

如何开发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方法的异步式调用,定义以下的委托:

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的各种异步操作

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐