asp.net 2.0页面性能的考虑--异步页面处理模型
一般情况下我们通常在一个页面中,结合页面事件处理模型,按顺序一步一步的声明各个处理过程。这就是所说的同步处理(Synchronous),我们可以很方便的定义页面处理的顺序。同步处理虽然很方便,但在耗时高请求处理的时候就会暴露严重的问题,在一个高并发的网站生产服务器上,由于.net处理是基于所谓的ThreadPool的,而Threadpool中的处理线程又是有限的,如果当前ThreadPool中的线程已经全部用尽,而后继又有不断的大规模请求的话,服务器的性能就会严重的下降,更严重的还会使服务器崩溃。所以我们关心最多的就是怎样来更好的使用的.net ThreadPool。让.net更好的发挥的性能。
说到这就不得不谈.net的异步处理(Asynchronous),模型一般为BeginXXX,EndXXX,BeginXXX返回一个IAsycResult对象,其中包含对当前异步操作的信息,而EndXXX用户接受返回值,输出参数。.net从ThreadPool中分配一个空闲线程给BeginXXX然后立即返回给ThreadPool,等到异步处理结束时,.net又从ThreadPool中分配一个空闲线程用于处理EndXXX方法。这样就可能有足够的线程用于处理其它的事情。见于篇幅问题这里我们只谈asp.net 2.0的异步处理模型,我们都知道在1.x的时候要很好的建立页面的异步处理是比较麻烦的,可能微软很早就认识到了这一点,所以在2.0的版本中引入的新的页面处理模型,加入了方便的异步处理点(Asynchronous Point)在PreRender事件和PreRenderComplete事件之间,在这个异步点,页面需要等待所有的异步处理都完成,所以可以在页面呈现之前完成你需要的任何效果,极大简化了建立异步页面的方法。
首先你要在建立页面的@page声明中加入 Async="true",这是必须的。设置告诉asp.net要选用IHttpAsyncHandler来处理当前的页面。接下来你需要做的一般是在Page_Load事件处理中使用异步处理过程,现在有两种途径:
第一种就是使用Page.AddOnPreRenderCompleteAsync方法来处理需要异步处理的Begin和End方法集.如下:
AddOnPreRenderCompleteAsync(new BeginEventHandler(MyBeginMethod),new EndEventHandler(myEndMethod));
通过添加上面的方法后,页面执行通常的生命周期事件直到页面的PreRender事件触发.然后Asp.net调用先前在AddOnPreRenderCompleteAsync中的注册的Begin处理程序。通常在Begin处理程序中处理的是一些异步的web服务,IO以及SQL的处理,这样就可以极大的缓解.net线程池的压力。例如:
<%@ Page Async="true" Language="C#" %>
<asp:Content ID="Content" ContentPlaceHolderID="Main" Runat="server">
<asp:Label ID="Output" Runat="server"></asp:Label>
</asp:Content>
public partial class AsyncPage : System.Web.UI.Page
{
private WebRequest m_Request;
void Page_Load (object sender, EventArgs e)
{
AddOnPreRenderCompleteAsync (
new BeginEventHandler(BeginAsyncOperation),
new EndEventHandler (EndAsyncOperation)
);
}
IAsyncResult BeginAsyncOperation (object sender, EventArgs e, AsyncCallback cb, object state)
{
m_Request = WebRequest.Create("http://www.dofor.cn");
return m_Request.BeginGetResponse (cb, state);
}
void EndAsyncOperation (IAsyncResult ar)
{
string text;
using (WebResponse response = m_Request.EndGetResponse(ar))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
text = reader.ReadToEnd();
}
}
Regex regex = new Regex ("href\\s*=\\s*\"([^\"]*)\"", RegexOptions.IgnoreCase);
MatchCollection matches = regex.Matches(text);
StringBuilder builder = new StringBuilder(1024);
foreach (Match match in matches)
{
builder.Append (match.Groups[1]);
builder.Append("<br/>");
}
Output.Text = builder.ToString ();
}
}
第二中就是注册异步任务(Register Asynchronous Task).RegisterAsyncTask比AddOnPreRenderCompleteAsync具有更大的灵活性和更多的优势。它可以允许你声明一个超时参数,同样可以声明在@Page中如:AsyncTimeout="5",以秒为单位,但要注意的是这里声明不是每个异步处理过程的超时而是整个页面的处理时间超时。同样.net framework 2.0还为注册任务引入了新的MethodAsync,MethodAsync就是为了方便处理多个异步过程的。例如:
<%@ Page Async="true" Language="C#" %>
<asp:Content ID="Content" ContentPlaceHolderID="Main" Runat="server">
<asp:Label ID="Output" Runat="server"></asp:Label>
</asp:Content>
public partial class AsyncPageTask : System.Web.UI.Page
{
private WebRequest m_Request;
protected void Page_Load(object sender, EventArgs e)
{
PageAsyncTask task = new PageAsyncTask(
new BeginEventHandler(BeginAsyncOperation),
new EndEventHandler(EndAsyncOperation),
new EndEventHandler(TimeoutAsyncOperation),
null
);
RegisterAsyncTask(task);
}
IAsyncResult BeginAsyncOperation(object sender, EventArgs e, AsyncCallback cb, object state)
{
m_Request= WebRequest.Create("http://www.dofor.cn");
return m_Request.BeginGetResponse(cb, state);
}
void EndAsyncOperation(IAsyncResult ar)
{
string text;
using (WebResponse response = m_Request.EndGetResponse(ar))
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
text = reader.ReadToEnd();
}
}
Regex regex = new Regex("href\\s*=\\s*\"([^\"]*)\"", RegexOptions.IgnoreCase);
MatchCollection matches = regex.Matches(text);
StringBuilder builder = new StringBuilder(1024);
foreach (Match match in matches)
{
builder.Append(match.Groups[1]);
builder.Append("<br/>");
}
Output.Text = builder.ToString();
}
void TimeoutAsyncOperation(IAsyncResult ar)
{
Output.Text = "当前数据不可用";
}
}
总之合理的利用asp.net 2.0的异步处理,就可以极大的改善您大吞吐量高并发网站页面的性能。