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之前的那點事兒系列文章,包括了原創,翻譯,轉載等各類型的文章,如果對你有用,請推薦支持一把,給大叔寫作的動力。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM