上面两篇文章说了http协议和IIS处理,这次说下当IIS把请求交给Asp.net后的过程。

AppManagerAppDomainFactory

  1. 当IIS把请求交给asp.net时候,如果AppDomain还不存在则创建APPDomain,将AppDomain指派给与请求对应的应用程序,这通过AppManagerAppDomainFactory类中的Create方法实现,代码如下:

    public Object Create(String appId, String appPath) {
       try {
    
           if (appPath[0] == '.') {
               System.IO.FileInfo file = new System.IO.FileInfo(appPath);
               appPath = file.FullName;
           }
           if (!StringUtil.StringEndsWith(appPath, '\\')) {
               appPath = appPath + "\\";
           }
    
           ...
    
           ISAPIApplicationHost appHost = new ISAPIApplicationHost(appId, appPath,false);
           //创建环境,包括编译环境
           ISAPIRuntime isapiRuntime = (ISAPIRuntime)_appManager.CreateObjectInternal(appId, typeof(ISAPIRuntime), appHost,  false, null);
           isapiRuntime.StartProcessing();
    
           return new ObjectHandle(isapiRuntime);
    
       }
       catch (Exception e) {
             ...
       }
       }   
  2. 创建完成后,非托管代码开始调用 ISAPIRuntime 中ProcessRequest方法(通过COM调用 )

ISAPIRuntime--asp.net入口

  1. 首先看下ISAPIRuntime中的ProcessRequest方法签名
    public int ProcessRequest(IntPtr ecb, int iWRType);
  1. ProcessRequest有两个参数,一个是请求报文的ecb句柄,一个请求的类型,在运行的过程中,ecb首先被再次封装成托管资源的请求报文wr。 把封装好的代码传递给HttpRuntime类中的ProcessRequestNoDemand. 核心代码如下:
    bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP);
    wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
    wr.Initialize();

    // check if app path matches (need to restart app domain?)
    String wrPath = wr.GetAppPathTranslated();
    String adPath = HttpRuntime.AppDomainAppPathInternal;

    if (adPath == null ||
        StringUtil.EqualsIgnoreCase(wrPath, adPath))
    {

        HttpRuntime.ProcessRequestNoDemand(wr);
        return 0;
    }
    else
    {
        // need to restart app domain
        HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged,
                                    SR.GetString(SR.Hosting_Phys_Path_Changed,
                                                                        adPath,
                                                                        wrPath));
        return 1;
    }

HttpRuntime

  1. HttpRuntime收到传递过来的HttpWorkerRequest类的实例对象wr,通过调用当前类中的ProcessRequestNow方法,把参数传递给ProcessRequestInternal(ProcessRequestNow的调用了ProcessRequestInternal)。
    internal static void ProcessRequestNoDemand(HttpWorkerRequest wr) {
        RequestQueue rq = _theRuntime._requestQueue;

        wr.UpdateInitialCounters();

        if (rq != null)  // could be null before first request
            wr = rq.GetRequestToExecute(wr);

        if (wr != null) {
            CalculateWaitTimeAndUpdatePerfCounter(wr);
            wr.ResetStartTime();
            ProcessRequestNow(wr);
        }
    }

    internal static void ProcessRequestNow(HttpWorkerRequest wr) {
        _theRuntime.ProcessRequestInternal(wr);
    }
  1. 在ProcessRequestInternal中,创建了HttpContext和HttpApplication对象实例,核心代码如下

    private void ProcessRequestInternal(HttpWorkerRequest wr) {

        ...

    // Construct the Context on HttpWorkerRequest, hook everything together
    HttpContext context;

    try {
        context = new HttpContext(wr, false /* initResponseWriter */);
    }
    catch {
        ...
    }

    ...

    try {
            ...
        // Get application instance
        IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);

        if (app == null)
            throw new HttpException(SR.GetString(SR.Unable_create_app_object));

            ... 

        if (app is IHttpAsyncHandler) {
            // asynchronous handler
            IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
            context.AsyncAppHandler = asyncHandler;
            asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
        }
        else {
            // synchronous handler
            app.ProcessRequest(context);
            FinishRequest(context.WorkerRequest, context, null);
        }
    }
    catch (Exception e) {
        ...
    }
    }
  1. 在ProcessRequestInternal方法的内部,实现对HttpContext类和HttpApplicationFactory的对象实例的创建,核心代码: 根据上面代码,当获得HttApplication对象后,判断是否是IHttpAsyncHandler类型,如果是则调用BeginProcessRequest方法,此处的if条件是一直成立的,因为HttpApplication实现了IHttpAsyncHandler接口,而ProcessRequest方法的实现也仅仅是抛出了一个异常,笔者觉得此处应该是微软留了一个扩展的地方。
   public class HttpApplication:IComponent,IHttpAsyncHandler, IRequestCompletedNotifier, ISyncContext {
        ...

    }
   void IHttpHandler.ProcessRequest(HttpContext context) {
            throw new HttpException(SR.GetString(SR.Sync_not_supported));
        }

HttpContext对象

这个对象是一个请求响应的结合体,里面包含了HttpRequest和HttpResponse对象,在构造HttpContext对象时,同时也对HttpRequest和HttpResponse也进行了初始化,代码如下:

   internal HttpContext(HttpWorkerRequest wr, bool initResponseWriter) {
        _wr = wr;
        Init(new HttpRequest(wr, this), new HttpResponse(wr, this));

        if (initResponseWriter)
            _response.InitResponseWriter();

        PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_EXECUTING);
    }

创建HttpApplication

通过HttpApplicationFactory中的静态方法GetApplicationInstance来获得实例对象(常用的工厂模式),在创建对象的时候调用了 _theApplicationFactory.GetNormalApplicationInstance(context);方法(其中context形参是上文创建的HttpContext)来执行实例化操作,核心代码如下:


  internal static IHttpHandler GetApplicationInstance(HttpContext context) {
       if (_customApplication != null)
           return _customApplication;

       // Check to see if it's a debug auto-attach request
       if (context.Request.IsDebuggingRequest)
           return new HttpDebugHandler();

       _theApplicationFactory.EnsureInited();

       _theApplicationFactory.EnsureAppStartCalled(context);

       return _theApplicationFactory.GetNormalApplicationInstance(context);
   }
        

这个方法里有三个方法的调用,分别是:

i. _theApplicationFactory.EnsureInited()

主要功能是对Global.asxc文件进行编译和处理,并反射出对其中的事件,放到ArrayList中,核心代码如下:

  • 找到global.asax路径进行编译

     private void Init() {
           if (_customApplication != null)
                   return;
               try {
                   try {
                       _appFilename = GetApplicationFile();
                       CompileApplication();
                   }
                   finally {
                       SetupChangesMonitor();
                   }
               }
               catch {
                   throw;
               }
      }
  • 调用ReflectOnApplicationType方法把事件装入ArrayList

   private void CompileApplication() {
           _theApplicationType = BuildManager.GetGlobalAsaxType();
            BuildResultCompiledGlobalAsaxType result = BuildManager.GetGlobalAsaxBuildResult();
            if (result != null) {
                if (result.HasAppOrSessionObjects) {
                    GetAppStateByParsingGlobalAsax();
                }
                _fileDependencies = result.VirtualPathDependencies;
            }

            if (_state == null) {
                _state = new HttpApplicationState();
            }
            ReflectOnApplicationType();
        }

     

   private void ReflectOnApplicationType() {
        ArrayList handlers = new ArrayList();
        MethodInfo[] methods;
        // get this class methods
        methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
        foreach (MethodInfo m in methods) {
            if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                handlers.Add(m);
        }
        Type baseType = _theApplicationType.BaseType;
        if (baseType != null && baseType != typeof(HttpApplication)) {
            methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
            foreach (MethodInfo m in methods) {
                if (m.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                    handlers.Add(m);
            }
        }
        _eventHandlerMethods = new MethodInfo[handlers.Count];
        for (int i = 0; i < _eventHandlerMethods.Length; i++)
            _eventHandlerMethods[i] = (MethodInfo)handlers[i];
    }

ii. _theApplicationFactory.EnsureAppStartCalled(context)

创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。 具体实现:

private void EnsureAppStartCalled(HttpContext context) {
 if (!_appOnStartCalled) {
     lock (this) {
       if (!_appOnStartCalled) {
          using (new DisposableHttpContextWrapper(context)) {
               // impersonation could be required (UNC share or app credentials)

               WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);

               // fire outside of impersonation as HttpApplication logic takes
               // care of impersonation by itself
               FireApplicationOnStart(context);
           }
           _appOnStartCalled = true;
        }
     }
   }
 }  

iii. _theApplicationFactory.GetNormalApplicationInstance(context);

主要是获得HttpApplication实例,首先从队列中去取,如果取出为空,则利用反射创建,调用InitInternal方法

 private HttpApplication GetNormalApplicationInstance(HttpContext context) {
         HttpApplication app = null;
         lock (_freeList) {
             if (_numFreeAppInstances > 0) {
                 app = (HttpApplication)_freeList.Pop();
                 _numFreeAppInstances--;

                 if (_numFreeAppInstances < _minFreeAppInstances) {
                     _minFreeAppInstances = _numFreeAppInstances;
                 }
             }
         }
         if (app == null) {
             // If ran out of instances, create a new one
             app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
             using (new ApplicationImpersonationContext()) {
                // 调用BuildSteps和获得所有的HttpModule
                 app.InitInternal(context, _state, _eventHandlerMethods);
             }
         }
         if (AppSettings.UseTaskFriendlySynchronizationContext) {
             // When this HttpApplication instance is no longer in use, recycle it.
             app.ApplicationInstanceConsumersCounter = new CountdownTask(1); // representing required call to HttpApplication.ReleaseAppInstance
             app.ApplicationInstanceConsumersCounter.Task.ContinueWith((_, o) => RecycleApplicationInstance((HttpApplication)o), app, TaskContinuationOptions.ExecuteSynchronously);
         }
         return app;
     }

从代码中可以分析到,在HttpApplication创建的过程中,是有一个_freeList的一个堆栈来控制的。当对象创建成功后,执行app.InitInternal(context, _state, _eventHandlerMethods)来进行后续的操作。整个的代码流程,可以理解成以下过程:

源码git地址:https://github.com/fuwei199006/Source/tree/master/dotnet46/Source

写于 2017.03.07

第36篇 Asp.Net源码解析(一)的更多相关文章

  1. 第37篇 Asp.Net源码解析(二)--详解HttpApplication

    这篇文章花了点时间,差点成烂到电脑里面,写的过程中有好几次修改,最终的这个版本也不是很满意,东西说的不够细,还需要认真的去看下源码才能有所体会,先这样吧,后面有时间把细节慢慢的再修改.顺便对于开发的学 ...

  2. ExcelReport第二篇:ExcelReport源码解析

    导航 目   录:基于NPOI的报表引擎——ExcelReport 上一篇:使用ExcelReport导出Excel 下一篇:扩展元素格式化器 概述 针对上一篇随笔收到的反馈,在展开对ExcelRep ...

  3. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  4. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  5. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  6. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

  7. jQuery2.x源码解析(DOM操作篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) jQuery这个类库最为核心重要的功能就是DOM ...

  8. ExcelReport源码解析

    ExcelReport第二篇:ExcelReport源码解析   导航 目   录:基于NPOI的报表引擎——ExcelReport 上一篇:使用ExcelReport导出Excel 下一篇:扩展元素 ...

  9. Laravel源码解析--看看Lumen到底比Laravel轻在哪里

    在前面一篇<Laravel源码解析--Laravel生命周期详解>中我们利用xdebug详细了解了下Laravel一次请求中到底做了哪些处理.今天我们跟 Lumen 对比下,看看 Lume ...

随机推荐

  1. 2.7. 属性的各种设置选项(Core Data 应用程序实践指南)

    可供配置的选项根据属性类型的不同有所变化,并不是每一种属性都能配置下列选项 Transient:勾选该选项,表示该属性不写入“持久化存储区”,这听起来很奇怪,但有时候,只需要把特性留在托管对象上下文就 ...

  2. 读jQuery之二十(Deferred对象)

    Deferred对象是由$.Deferred构造的,$.Deferred被实现为简单工厂模式. 它用来解决JS中的异步编程,它遵循 Common Promise/A 规范.实现此规范的还有 when. ...

  3. js判断ip地址,子网掩码,网关的逻辑性检查

    因为要做静态地址配置的js校验,找了好多资料发现网上都是关于ip,mask的有效性检查,没有ip,submask,gateway的逻辑性判断,自己写下代码供需要的人参考. 普及下网关地址知识: 就是进 ...

  4. sitemap.xml 的 几个东西

    https://github.com/PureKrome/SimpleSitemap/wiki/Sitemap-Index-example  简单类实现 支持sitemapindex 有说明向导 ht ...

  5. aix下java程序运行问题

    CLASSPATH=/track.jar:/standalone.jar export CLASSPATH nohup /usr/java6_64/bin/java com.TrackMain > ...

  6. linux系统下find删除目录下除一文件外的所有文件

    /data/目录下有a.txt b.txt c.txt d.txt删除/data/目录下所有文件,保留b.txt两种方法:1.[root@xuegod62 ~]# find /data/ -type ...

  7. 实现jul 日志重定向到 slf4j

    需求背景 jul 指的是java.util.logging,是 java 内置的日志模块,目前流行的Java日志组件还包括 jcl(common-logging).slf4j/log4j/logbac ...

  8. javascript设计模式与开发实践阅读笔记(11)—— 模板方法模式

    模板方法模式: 由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类.通常在抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有方法的执行顺序.子类通过继承这个抽象类,也继 ...

  9. 蓝牙连接音响问题(android电视)

    最近老大让我开发电视的蓝牙,由于android电视的蓝牙不稳定和设计上的各种各样的要求,需要在原有的基础上做一些更改,中间遇到了各种问题,在此总结一下. 我们首先要获取blueToothAdapter ...

  10. 龙珠超的新OP【限界突破×サバイバー】

    这首歌真的很燃 下载>>   限界突破×サバイバー 中文歌词 演唱:冰川清志 兴奋了!就去宇宙吧 最先端的“着迷”怎么样! 握在手中 突然想要大笑 糊里糊涂也习惯了吗! I can't g ...