【InsistYML】ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part I
上一篇 / 下一篇 2008-07-24 14:17:42 / 个人分类:.net
一、从Unmanaged Environment到Managed Environment
上一部分我们说到IIS收到一个基于ASP.NET资源文件的访问,它会把Http Request交给一个ASP.NET ISAPI Extension处理。ASP.NET ISAPI会加载CLR,从而创建一个托管的环境。ASP.NET ISAPI Extension定义在一个名为aspnet_isapi.dll中,aspnet_isapi.dll是一个纯Native的、高效的Dll,也就是说,虽然ASP.NET ISAPI通过加载CLR创建一个托管的环境,但是ASP.NET ISAPI本省却运行在一个Unmanaged的环境中。而我们的ASP.NET Application确是完全的Managed code,运行在一个Managed的环境中。要了解ASP.NET Http Runtime Pipeline这个纯托管的Runtime,我们必须先了解从Unmanaged Environment到Managed Environment的这道桥梁。
#P~i:d;D*C0
N!\!T&V'I$[,j/Uzs0
-m+];\6Z7vk T0上图简单表述了在IIS 6环境下,从非托管环境到托管环境的过程。从图中我们可以看到,ASP.NET ISAPI运行在一个非托管环境之中。ASP.NET ISAPI经过系列COM级别的class调用(由于这些被调用的Class都是一个个undocumented class,所以要真正说清楚调用流程中每个具体的细节是一件很难的事情,而且也确实没有很大的必要去挖掘它,因为具体的实现可能会经常变动,如果对此具有好奇心的朋友可以通过一些Tool,比如Reflector去仔细研究一下),最终的调用降临到一个托管的、继承自System.Web.Hosting.ISAPIRuntime类的对象上。ISAPIRuntime是一个特殊的class,他实现了InterfaceSystem.Web.Hosting.IISAPIRuntime。下面是该Interface的定义。通过定义我们可以看到,这是一个基于COM的Interface,也就是说Caller可以通过COM的方式调用实现该Interface的Class的对象。在这里,这个最初的Caller就是ASP.NET ISAPI。从这里我们可以总结出:ASP.NET ISAPI通过调用System.Web.Hosting.ISAPIRuntimeInstance的ProcessRequest方法,进而从非托管的环境进入了托管的环境。
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1")]ITPUB个人空间fF%SG;H+@k
public interface IISAPIRuntimeITPUB个人空间JNPz]2[

{R/KN(r[*P2d0
void StartProcessing();*SL%Y1xM_e.i2_6B0
void StopProcessing();ITPUB个人空间-G~:E&jX;wse:i+[Y
[return: MarshalAs(UnmanagedType.I4)]q-{ e |KNi`:l-D*E0
int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel);ITPUB个人空间3Oz'V|*?l;b
void DoGCCollect();ITPUB个人空间:fre3@WE QT5IT
}(](uUjtn*Y({0

ISAPI ECB (Execution Control Block) & ISAPIWorkerRequest
sAsr4^0S0ITPUB个人空间+cK9]*P
O1l9_l7R
通过System.Web.Hosting.IISAPIRuntime Interface中的ProcessRequest方法的Siganature,我们可以看出该方法包含两个参数,其中一个是名为ecb的Unmanaged Pointer,另一个是useProcessModel。ECB全称是Execution Control Block,在整个Http Request Processing过程中起着非常重要的作用,我们现在来简单介绍一个ECB。
ISAPI顾名思义,就是实现了一些基于Internet Server的API。Aspnet_isapi.dll实现了这些API,对于IIS来说,它可以调用这些API进入托管的环境实现对ISAPIRuntime的调用,对于ISAPIRuntime来说,它需要调用ASP.NET ISAPI实现一些必要的功能,比如获得Server Variable的数据,获得通过Post Mehod传回Server的数据;以及最终将Response的内容返回给ASP.NET ISAPI,并通过ASP.NET ISAPI返回到Client。一般地ISAPIRuntime不能直接调用ASP.NET ISAPI,而是通过一个对象指针实现对其的调用,这个对象就是ECB,ECB实现了对ISAPI的访问。
还有一点特别需要强调的是,ISAPI对ISAPIRutime的调用是异步的,也就是说ISAPI调用ISAPIRutime之后立即返回。这主要是出于Performance和Responsibility考虑的,因为ASP.NET Application天生就是一个多线程的应用,为了具有更好的响应能力,异步操作是最有效的解决方式。但是这里就会有一个问题,我们知道我们对ASP.NET资源的调用本质上是一个Request/Response的Message Exchange Pattern,异步调用往往意味着ISAPI将Request传递给ISAPIRuntime,将不能得到ISAPIRuntime最终生成的Response,这显然是不能接受的。而ECB解决了这个问题,ISAPI在调用ISAPIRutime的ProcessRequest方法时会将自己对应的ECB的指针传给它,ISAPIRutime不但可以将最终生成的Response返回给ISAPI,还能通过ECB调用ISAPI获得一些所需的数据。
明白ECB是怎么回事之后,我们通过Reflector简单了解一下ISAPIRutime的ProcessRequest的实现:
AW*R ]#K W.i0
public int ProcessRequest(IntPtr ecb, int iWRType)A,woR$q0


{W#I*T/VUi2E0
IntPtr zero = IntPtr.Zero;ITPUB个人空间mdW|3nd)o&g
if (iWRType == 2)ITPUB个人空间L,nT iC;w#~
{)gZtf7P(T,e6~0
zero = ecb;^\^i\1f0
ecb = UnsafeNativeMethods.GetEcb(zero);-f'~1dK/U0
}ITPUB个人空间 O
\'_ S'O2S
ISAPIWorkerRequest wr = null;ITPUB个人空间,P.i.x`reYx
try:u3SaO:xBpr L0

{ITPUB个人空间(RcVogE6O
bool useOOP = iWRType == 1;~'^f3U!?N0
wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);ITPUB个人空间 G5I0Tcr
wr.Initialize();c*S,?(G"k"b0
string appPathTranslated = wr.GetAppPathTranslated();6j0IcXO7Ts2B0
string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;ITPUB个人空间NZKQ1pI
PUaVJrb
if ((appDomainAppPathInternal == null) || StringUtil.EqualsIgnoreCase(appPathTranslated, appDomainAppPathInternal))7Z l6Pl@"x3ll0

{ITPUB个人空间r;sd~s(\6K
HttpRuntime.ProcessRequestNoDemand(wr);ITPUB个人空间
e3Vh}x^:L
return 0;ITPUB个人空间pM.jn3o3wE
}} U/iK${$z\0

HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString("Hosting_Phys_Path_Changed", new object[]
{ appDomainAppPathInternal, appPathTranslated }));ITPUB个人空间lnP[$bL6G%o:^8c{
return 1;ITPUB个人空间f#DR
K)H
}g7aZX5I!l:l0
catch (Exception exception)ITPUB个人空间g:K8nrx
D
{ITPUB个人空间`#[7Nj5b%R&y
tryL5`!UBp(C$M0

{rfi#_8{b0
WebBaseEvent.RaiseRuntimeError(exception, this);h/o _7t7i8h+A0W S0
}ITPUB个人空间Fj(w
LA3Q:P
catchB\0}L)z"gd]h0

{yyn_*NC0j}4i0
}ITPUB个人空间:l$D+?jL]Rz
if ((wr == null) || (wr.Ecb != IntPtr.Zero))ITPUB个人空间_ah4q}
~!pY
{ITPUB个人空间$lS:Be:pv.u.rK4a
throw;ITPUB个人空间0J!s
]\~#^{
}ITPUB个人空间?~(JX/YR
if (zero != IntPtr.Zero)