• MVC之前的那点事儿系列(4):Http Pipeline详细分析(上)


    文章内容

    继续上一章节的内容,通过HttpApplicationFactory的GetApplicationInstance静态方法获取实例,然后执行该实例的BeginProcessRequest方法进行执行余下的Http Pipeline 操作,代码如下:

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

    那GetApplicationInstance这个方法究竟做了啥呢?难道只是new一个新对象出来?感觉应该不像,那我们就来看看HttpApplicationFactory类的GetApplicationInstance静态方法源码:

    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); 
    }

    里面有3行代码我已经标记为粗体了,在解释每行代码的具体作用之前,先看看_theApplicationFactory对象实例从哪里来,通过查看该字段的声明代码可以看到它是单例的实现。

    // the only instance of application factory
    private static HttpApplicationFactory _theApplicationFactory = new HttpApplicationFactory();

    第一行粗体代码是执行,该实例的EnsureInited方法,这个方法会通过lock的方式调用Init方法(好处自然不用多说了吧),代码如下:

    private void EnsureInited() {
        if (!_inited) {
            lock (this) { 
                if (!_inited) {
                    Init(); 
                    _inited = true; 
                }
            } 
        }
    }

    通过查找 Init方法的代码以及其中2行如下代码里的细节,我们可以得知,这2行代码主要是从global.asax获取内容,然后进行编译。

    _appFilename = GetApplicationFile(); 
    CompileApplication();

    所以,HttpApplicationFactory._theApplicationFactory.EnsureInited()  的方法首先检查HttpApplicationFactory是否被初始化,如果没有,就通过HttpApplicationFactory.Init()进行初始化。在Init()中,先获取global.asax文件的完整路径,然后调用CompileApplication()对global.asax进行编译。

    第2行粗体的EnsureAppStartCalled方法,最终会调用如下的私有方法FireApplicationOnStart,代码如下:

    private void FireApplicationOnStart(HttpContext context) { 
        if (_onStartMethod != null) { 
            HttpApplication app = GetSpecialApplicationInstance();
     
            app.ProcessSpecialRequest(
                                        context,
                                        _onStartMethod,
                                        _onStartParamCount, 
                                        this,
                                        EventArgs.Empty, 
                                        null); 
    
            RecycleSpecialApplicationInstance(app); 
        }
    }

    通过代码我们能够得知HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context)  创建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的Application_Start(object sender, EventArgs e)方法。然后在处理完事件以后就立即被回收掉,因为系统初始化只需要一次,但是其中的GetSpecialApplicationInstance里会对IIS7做一些特殊的事情,我们后面的章节会讲到。

    第3行的粗体代码是我们这里要说的重点,它方法里的代码如下:

    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()) {
                app.InitInternal(context, _state, _eventHandlerMethods); 
            }
        }
    
        return app; 
    }

    如果在有空闲的HttpApplication实例,就直接用,如果没有就新创建,然后调用InitInternal方法进行初始化相关的内容,最后返回该HttpApplication实例。

    让我们来看看HttpApplication的核心方法InitInternal里都是干了什么事儿吧,先上代码,有点多,但是很值得:

    internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) { 
        Debug.Assert(context != null, "context != null");
    
        // Remember state
        _state = state; 
    
        PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES); 
     
        try {
            try { 
                // Remember context for config lookups
                _initContext = context;
                _initContext.ApplicationInstance = this;
     
                // Set config path to be application path for the application initialization
                context.ConfigurationPath = context.Request.ApplicationPathObject; 
     
                // keep HttpContext.Current working while running user code
                using (new DisposableHttpContextWrapper(context)) { 
    
                    // Build module list from config
                    if (HttpRuntime.UseIntegratedPipeline) {
     
                        Debug.Assert(_moduleConfigInfo != null, "_moduleConfigInfo != null");
                        Debug.Assert(_moduleConfigInfo.Count >= 0, "_moduleConfigInfo.Count >= 0"); 
     
                        try {
                            context.HideRequestResponse = true; 
                            _hideRequestResponse = true;
                            InitIntegratedModules();
                        }
                        finally { 
                            context.HideRequestResponse = false;
                            _hideRequestResponse = false; 
                        } 
                    }
                    else { 
                        InitModules();
    
                        // this is used exclusively for integrated mode
                        Debug.Assert(null == _moduleContainers, "null == _moduleContainers"); 
                    }
     
                    // Hookup event handlers via reflection 
                    if (handlers != null)
                        HookupEventHandlersForApplicationAndModules(handlers); 
    
                    // Initialization of the derived class
                    _context = context;
                    if (HttpRuntime.UseIntegratedPipeline && _context != null) { 
                        _context.HideRequestResponse = true;
                    } 
                    _hideRequestResponse = true; 
    
                    try { 
                        Init();
                    }
                    catch (Exception e) {
                        RecordError(e); 
                    }
                } 
     
                if (HttpRuntime.UseIntegratedPipeline && _context != null) {
                    _context.HideRequestResponse = false; 
                }
                _hideRequestResponse = false;
                _context = null;
                _resumeStepsWaitCallback= new WaitCallback(this.ResumeStepsWaitCallback); 
    
                // Construct the execution steps array 
                if (HttpRuntime.UseIntegratedPipeline) { 
                    _stepManager = new PipelineStepManager(this);
                } 
                else {
                    _stepManager = new ApplicationStepManager(this);
                }
     
                _stepManager.BuildSteps(_resumeStepsWaitCallback);
            } 
            finally { 
                _initInternalCompleted = true;
     
                // Reset config path
                context.ConfigurationPath = null;
    
                // don't hold on to the context 
                _initContext.ApplicationInstance = null;
                _initContext = null; 
            } 
        }
        catch { // Protect against exception filters 
            throw;
        }
    }

    该代码主要有2个功能,一个是初始化大家熟悉的HttpModules,一个是通过BuildSteps执行20多个生命周期事件的处理函数(这部分内容,我们将在下一章节详细讲解Http Pipeline)。通过上面的代码我们可以看出,每个功能都有一个特殊判断,判断IIS是否是IIS7的集成模式,如果是就有特殊的步骤,如果不是就走一般的步骤,两者直接的差异分别是:IIS7初始化HttpModules的时候会从网站配置的Modules里读取(因为IIS7预加载CLR和大批量Modules),BuildSteps的时候, IIS7集成模式走的是自己特殊的流程(加载服务器上的HttpModules)。

    让我们先总结一下再看代码,InitInternal方法的主要功能如下:

    1. InitModules():根据Web.Config的设置,加载相应的HttpModules。
    2. InitIntegratedModules():会加载IIS7集成模式下在服务器上设定的HttpModuels和Web.config里system.webserver下的HttpModuels。
    3. HookupEventHandlersForAppplicationAndModules:根据发生的事件,调用HttpApplication实例中相应的事件处理函数。
    4. 创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中,等待回调时执行。从这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。

    至此,除了20多个周期事件和Handler相关的代码我们没有讲解,其它和HttpApplication相关的并且对我们有帮助的,已经差不多清晰了。关于20多个周期事件和执行Handler方面的内容,我们下一章节再做详细解释。

    参考资料:

    http://msdn.microsoft.com/en-us/magazine/cc188942.aspx

    http://msdn.microsoft.com/en-us/library/bb470252.aspx

    http://www.cnblogs.com/zhaoyang/archive/2011/11/16/2251200.html

    同步与推荐

    本文已同步至目录索引:MVC之前的那点事儿系列

    MVC之前的那点事儿系列文章,包括了原创,翻译,转载等各类型的文章,如果对你有用,请推荐支持一把,给大叔写作的动力。

  • 相关阅读:
    U盘安装Ubuntu 10.4 Server
    MySQL操作使用
    Fedora17安装MySQL及配置
    笔记:重构
    Java 并发之线程安全
    Java 并发之共享对象
    UTF8 与 UTF16 编码
    matplotlib 初使用
    用 ggplot2 在同一个图上画多条颜色不同的线
    完成情况(一)
  • 原文地址:https://www.cnblogs.com/TomXu/p/3756832.html
Copyright © 2020-2023  润新知