Asp.net Process Model 之二:Asp.net Runtime Pipeline
在第一篇Asp.net Process Model 之一:IIS 和 ISAPI 中学习了用户请求如何从IIS通过ISAPI将request传送到asp.net Http Runtime中的,这节学习在asp.net runtime 内部是如何处理request的。
一、从unmanaged environment到managed environment
Asp.net ISAPI extension(aspnet_isapi.dll)创建aspnet_wp进程用于运行Asp.net Application,初始化aspnet_wp时加载CLR,从而创建一个managed运行环境,但是aspnet_isapi.dll是native的。那么Asp.net ISAPI extension是如何从unmanaged environment转换到managed environment呢?
上篇提到,在初始化CLR时创建了ISPAPIRuntime实例,该实例实现IISAPIRuntime接口。ISAPIRuntime接口定义如下:
public interface IISAPIRuntime
{
void StartProcessing();
void StopProcessing();
[return: MarshalAs(UnmanagedType.I4)]
int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel);
void DoGCCollect();
}
从接口定义可看出,该接口是COM可见的,非托管代码可以通过COM调用方式调用实现了interface的类的实例。
ECB(Exective Control Block)
ProcessRequest函数有两个参数,其中一个是指针参数ecb。ecb是ISAPIRuntime类对aspnet_isapi.dll的引用,用于从aspnet_isapi.dll中提取请求信息,并将响应结果传回给aspnet_isapi.dll。
ISAPIRuntime的ProcessRequest函数定义如下:
{
IntPtr zero = IntPtr.Zero;
if (iWRType == 2)
{
zero = ecb;
ecb = UnsafeNativeMethods.GetEcb(zero);
}
ISAPIWorkerRequest wr = null;
try
{
bool useOOP = iWRType == 1;
wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
wr.Initialize();
string appPathTranslated = wr.GetAppPathTranslated();
string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))
{
HttpRuntime.ProcessRequestNoDemand(wr);
return 0;
}
HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[] { appDomainAppPathInternal, appPathTranslated }));
return 1;
}
catch (Exception exception)
{
try
{
WebBaseEvent.RaiseRuntimeError(exception, this);
}
catch
{
}
if ((wr == null) || !(wr.Ecb == IntPtr.Zero))
{
throw;
}
if (zero != IntPtr.Zero)
{
UnsafeNativeMethods.SetDoneWithSessionCalled(zero);
}
if (exception is ThreadAbortException)
{
Thread.ResetAbort();
}
return 0;
}
}
此函数中重要之处有两点:
ISAPIWorlerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb,useOOP)
HttpRuntime.ProcessRequestNoDemand(wr);
ISAPIWorkerRequest继承自HttpWorkerRequet抽象类,此抽象类定义由 ASP.NET 托管代码用于处理请求的基本辅助方法和枚举。HttpRuntime.ProcessRequest 静态方法驱动所有 ASP.NET Web 处理执行。
aspnet_isapi.dll就是调用了ISAPIRuntime的ProcessRequest函数,从unmanaged environment转到managed environment。该函数根据aspnet_isapl指针创建新请求ISAPIWokerRequest,并将此请求对象作为参数传递给HttpRuntime.ProcessRequestNoDemand,开始进入asp.net的请求处理流程(Asp.net Runtime Pileline)。
二、Asp.net Runtime Pileline
下图显示了进入托管环境的Asp.net Runtime PipleLine后的执行情况
数据流程图为:
HttpRuntime函数内依次执行HttpRuntime下面的三个分支操作。下面分别介绍各处理流程
启动请求处理
HttpRuntime的ProcessRequestNoDemand静态方法启动web处理,该方法会调用一系列内部函数。其中几个重要的函数有:
{
RequestQueue queue = _theRuntime._requestQueue;
if (queue != null)
{
wr = queue.GetRequestToExecute(wr);
}
if (wr != null)
{
CalculateWaitTimeAndUpdatePerfCounter(wr);
wr.ResetStartTime();
ProcessRequestNow(wr);
}
}
该方法从运行时的请求队列中取出一个请求后,继续执行其他操作。
internal static void ProcessRequestNow(HttpWorkerRequest wr)
{
_theRuntime.ProcessRequestInternal(wr);
}
private void ProcessRequestInternal(HttpWorkerRequest wr)
{
HttpContext context;
try
{
context = new HttpContext(wr, false);
}
catch
{
wr.SendStatus(400, "Bad Request");
wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");
byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
wr.SendResponseFromMemory(bytes, bytes.Length);
wr.FlushResponse(true);
wr.EndOfRequest();
return;
}
wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context);
Interlocked.Increment(ref this._activeRequestCount);
HostingEnvironment.IncrementBusyCount();
try
{
try
{
this.EnsureFirstRequestInit(context);
}
catch
{
if (!context.Request.IsDebuggingRequest)
{
throw;
}
}
context.Response.InitResponseWriter();
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
if (applicationInstance == null)
{
throw new HttpException(SR.GetString("Unable_create_app_object"));
}
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, applicationInstance.GetType().FullName, "Start");
}
if (applicationInstance is IHttpAsyncHandler)
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);
}
else
{
applicationInstance.ProcessRequest(context);
this.FinishRequest(context.WorkerRequest, context, null);
}
}
catch (Exception exception)
{
context.Response.InitResponseWriter();
this.FinishRequest(wr, context, exception);
}
}
这个函数中重要点:
HttpContext context = new HttpContext(wr, false);
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);
以及后面的XXProcessRequest方法
HttpContext
HttpContext封装有关个别 HTTP 请求的所有 HTTP 特定的信息。为继承 IHttpModule 和 IHttpHandler 接口的类提供了对当前 HTTP 请求的HttpContext对象的引用。该对象提供对请求的内部 Request、Response、Server、User等有关属性的访问。它的生命周期直到Request处理结束或处理超时。
HttpApplication
HttpApplication可以说是asp.net Application的体现。HttpApplication本身并不包含对Request的任何处理,它的工作方式是通过在不同阶段触发不同Event来调用我们注册的Event Hander。下面列出了HttpApplication所有的Event,并按照触发的时间先后顺序排列:
- BeginRequest:
- AuthenticateRequest & Post AuthenticateRequest
- AuthorizeRequest & Post AuthorizeRequest
- ResolveRequestCache & Post ResolveRequestCache
- PostMapRequestHandler:
- AcquireRequestState & AcquireRequestState:
- PreRequestHandlerExecute & Post RequestHandlerExecute:
- ReleaseRequestState & Post ReleaseRequestState
- UpdateRequestCache & PostUpdateRequestCache
- EndRequest:
HttpApplicationFactory.GetApplicationInstance创建一个基于Global.asax的应用程序实例。
{
if (_customApplication != null)
{
return _customApplication;
}
if (context.Request.IsDebuggingRequest)
{
return new HttpDebugHandler();
}
_theApplicationFactory.EnsureInited();
_theApplicationFactory.EnsureAppStartCalled(context);
return _theApplicationFactory.GetNormalApplicationInstance(context);
}
此方法经过一系列条件判断及初始化后,进入GetNormalApplicationInstance方法:
{
HttpApplication application = null;
lock (this._freeList)
{
if (this._numFreeAppInstances > 0)
{
application = (HttpApplication) this._freeList.Pop();
this._numFreeAppInstances--;
if (this._numFreeAppInstances < this._minFreeAppInstances)
{
this._minFreeAppInstances = this._numFreeAppInstances;
}
}
}
if (application == null)
{
application = (HttpApplication) HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
using (new ApplicationImpersonationContext())
{
application.InitInternal(context, this._state, this._eventHandlerMethods);
}
}
return application;
}
从方法定义看出,并不是直接创建一个HttpApplication对象,而是从一组空闲HttpAppllication实例数组中找一个来直接用,若没有空闲对象才创建新对象。由此可见,一个Asp.net Application并不是对应一个HttpApplication实例。这是因为,Asp.net是多线程的,要响应不同client的请求,如果一个Asp.net Application对应一个HttpApplication,则会对响应性能产生极大的影响。某个HttpApplication完成响应后也不会立即释放,而是放入空间队列供后续请求适用。
在创建新的HttpApplication时,会调用方法InitInternal,在此方法中包含一条执行语句:InitModules(),该方法的主要的目的就是查看Config中注册的所有HttpModule,并根据配置信息加载相应的Assembly,通过Reflection创建对应的HttpModule,并将这些Module加到HttpApplication 的 _moduleCollection Filed中。定义如下:
{
this._moduleCollection = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();
this.InitModulesCommon();
}
private void InitModulesCommon()
{
int count = this._moduleCollection.Count;
for (int i = 0; i < count; i++)
{
this._currentModuleCollectionKey = this._moduleCollection.GetKey(i);
this._moduleCollection[i].Init(this);
}
this._currentModuleCollectionKey = null;
this.InitAppLevelCulture();
}
从配置文件中找到所有HttpModule后,逐个调用HttpModule的Init方法,完成模块的初始化。
到目前,我们创建了响应request的HttpApplication实例,下一步就是响应客户端请求了。处理请求的命令在HttpRuntime的ProcessRequestInternal方法中
{
IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;
context.AsyncAppHandler = handler2;
handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);
}
else
{
applicationInstance.ProcessRequest(context);
this.FinishRequest(context.WorkerRequest, context, null);
}
前面提到,ISAPI方式异步调用ISAPIRuntime,这里的响应采用异步Handler(这句推断纯属自己瞎想,不知道是否属实)。HttpApplication的ProcessRequest方法不执行任何操作,仅仅是抛出异常throw new HttpException(SR.GetString("Sync_not_supported")),真正执行响应的是BeginProcessRequest方法:
{
this._context = context;
this._context.ApplicationInstance = this;
this._stepManager.InitRequest();
this._context.Root();
HttpAsyncResult result = new HttpAsyncResult(cb, extraData);
this.AsyncResult = result;
if (this._context.TraceIsEnabled)
{
HttpRuntime.Profile.StartRequest(this._context);
}
this.ResumeSteps(null);
return result;
}
在此方法中调用HttpRuntime.Profile.StartRequest启动请求。最终执行操作落入到函数InitRequest方法
{
int num2;
DataSet ds = _masterRequest.Clone();
DataRow row = this.NewRow(ds, "Trace_Request");
row["Trace_Time_of_Request"] = this._context.Timestamp.ToString("G");
string rawUrl = this._context.Request.RawUrl;
int index = rawUrl.IndexOf("?", StringComparison.Ordinal);
if (index != -1)
{
rawUrl = rawUrl.Substring(0, index);
}
row["Trace_Url"] = rawUrl;
row["Trace_Request_Type"] = this._context.Request.HttpMethod;
try
{
row["Trace_Request_Encoding"] = this._context.Request.ContentEncoding.EncodingName;
}
catch
{
}
if (this.TraceMode == TraceMode.SortByCategory)
{
ds.Tables["Trace_Trace_Information"].DefaultView.Sort = "Trace_Category";
}
this.AddRow(ds, "Trace_Request", row);
string[] allKeys = this._context.Request.Headers.AllKeys;
for (num2 = 0; num2 < allKeys.Length; num2++)
{
row = this.NewRow(ds, "Trace_Headers_Collection");
row["Trace_Name"] = allKeys[num2];
row["Trace_Value"] = this._context.Request.Headers[allKeys[num2]];
this.AddRow(ds, "Trace_Headers_Collection", row);
}
ArrayList list = this._context.Response.GenerateResponseHeaders(false);
int num3 = (list != null) ? list.Count : 0;
for (num2 = 0; num2 < num3; num2++)
{
HttpResponseHeader header = (HttpResponseHeader) list[num2];
row = this.NewRow(ds, "Trace_Response_Headers_Collection");
row["Trace_Name"] = header.Name;
row["Trace_Value"] = header.Value;
this.AddRow(ds, "Trace_Response_Headers_Collection", row);
}
allKeys = this._context.Request.Form.AllKeys;
for (num2 = 0; num2 < allKeys.Length; num2++)
{
row = this.NewRow(ds, "Trace_Form_Collection");
row["Trace_Name"] = allKeys[num2];
row["Trace_Value"] = this._context.Request.Form[allKeys[num2]];
this.AddRow(ds, "Trace_Form_Collection", row);
}
allKeys = this._context.Request.QueryString.AllKeys;
for (num2 = 0; num2 < allKeys.Length; num2++)
{
row = this.NewRow(ds, "Trace_Querystring_Collection");
row["Trace_Name"] = allKeys[num2];
row["Trace_Value"] = this._context.Request.QueryString[allKeys[num2]];
this.AddRow(ds, "Trace_Querystring_Collection", row);
}
if (HttpRuntime.HasAppPathDiscoveryPermission())
{
allKeys = this._context.Request.ServerVariables.AllKeys;
for (num2 = 0; num2 < allKeys.Length; num2++)
{
row = this.NewRow(ds, "Trace_Server_Variables");
row["Trace_Name"] = allKeys[num2];
row["Trace_Value"] = this._context.Request.ServerVariables.Get(allKeys[num2]);
this.AddRow(ds, "Trace_Server_Variables", row);
}
}
this._requestData = ds;
}
完成BeginProcessRequest后,HttpRuntime结束请求,进入方法FinishRequest
private void FinishRequest(HttpWorkerRequest wr, HttpContext context, Exception e)
{
HttpResponse response = context.Response;
if (EtwTrace.IsTraceEnabled(5, 1))
{
EtwTrace.Trace(EtwTraceType.ETW_TYPE_END_HANDLER, context.WorkerRequest);
}
SetExecutionTimePerformanceCounter(context);
if (e == null)
{
ClientImpersonationContext context2 = new ClientImpersonationContext(context, false);
try
{
response.FinalFlushAtTheEndOfRequestProcessing();
}
catch (Exception exception)
{
e = exception;
}
finally
{
if (context2 != null)
{
((IDisposable) context2).Dispose();
}
}
}
if (e != null)
{
if (this._appOfflineMessage != null)
{
try
{
response.StatusCode = 0x194;
response.OutputStream.Write(this._appOfflineMessage, 0, this._appOfflineMessage.Length);
response.FinalFlushAtTheEndOfRequestProcessing();
goto Label_00D6;
}
catch
{
goto Label_00D6;
}
}
using (new HttpContextWrapper(context))
{
ApplicationImpersonationContext context3 = new ApplicationImpersonationContext();
try
{
try
{
response.ReportRuntimeError(e, true, false);
}
catch (Exception exception2)
{
response.ReportRuntimeError(exception2, false, false);
}
response.FinalFlushAtTheEndOfRequestProcessing();
}
catch
{
}
finally
{
if (context3 != null)
{
((IDisposable) context3).Dispose();
}
}
}
}
Label_00D6:
this._firstRequestCompleted = true;
if (this._hostingInitFailed)
{
ShutdownAppDomain(ApplicationShutdownReason.HostingEnvironment, "HostingInit error");
}
int statusCode = response.StatusCode;
UpdatePerfCounters(statusCode);
context.FinishRequestForCachedPathData(statusCode);
wr.EndOfRequest();
HostingEnvironment.DecrementBusyCount();
Interlocked.Decrement(ref this._activeRequestCount);
if (this._requestQueue != null)
{
this._requestQueue.ScheduleMoreWorkIfNeeded();
}
}
此方法生成HttpResponse实例作为响应结果回送给Asp.net ISAPI
参考:
http://www.cnblogs.com/artech/archive/2007/09/13/891262.html