zoukankan      html  css  js  c++  java
  • ASP.NET初始化流程分析2

    上一篇讲了从创建应用程序域到创建ISAPIRuntime实例的过程,本篇继续讲Asp.net处理第一次请求的必要的初始化过程。

    ISAPIRuntime分析

    ISAPIRuntime在System.Web.Hosting中实现,它的ProcessRequest是我们处理web请求的入口。

        public int ProcessRequest(IntPtr ecb, int iWRType) {
            IntPtr pHttpCompletion = IntPtr.Zero;
            if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) {
                pHttpCompletion = ecb;
                ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion);
            }
            ISAPIWorkerRequest wr = null;
            try {
                bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP);
                wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
                wr.Initialize();
                String wrPath = wr.GetAppPathTranslated();
                String adPath = HttpRuntime.AppDomainAppPathInternal;            
                if (adPath == null ||StringUtil.EqualsIgnoreCase(wrPath, adPath)) {
                    HttpRuntime.ProcessRequestNoDemand(wr);
                    return 0;
                }
                else {
                     HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged, SR.GetString(SR.Hosting_Phys_Path_Changed, adPath, wrPath));
                     return 1;
                }
            }
            catch(Exception e) {
                try {
                    WebBaseEvent.RaiseRuntimeError(e, this);
                } catch {}
                if (wr != null && wr.Ecb == IntPtr.Zero) {
                    if (pHttpCompletion != IntPtr.Zero) {
                        UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion);
                    }
                    if (e is ThreadAbortException) {
                        Thread.ResetAbort();
                    }                   
                    return 0;
                 }
                throw;
            }
        }

    注意方法的IntPtr类型的参数ecb, 它是一个非托管的指针,用于传递一些必须的数据,以及最终将Response的内容返回给非托管环境ISAPI(异步方式),然后呈现给Client用户。方法中调用ISAPIWorkerRequest的静态方法CreateWorkerRequest而创建ISAPIWorkerRequest对象实例,参数分别为ecb和代表WorkerRequest类型的int参数iWRType,通过判断ecb和type类型的具体内容,来决定创建什么类型的WorkerRequest(上述类型的ISPAIWorkerRequest都继承于HttpWorkerRequest),上面的代码可以看出对不同版本的IIS进行了不同的包装,通过其Initialize方法来初始化一些基本的信息(比如:contentType, querystring的长度,filepath等相关信息)。然后调用HttpRuntime.ProcessRequestNoDemand(wr)转入HttpRuntime处理请求,最终体现在调用ProcessRequestInternal方法上。

    HttpRuntime分析

    Httpruntime在System.Web下实现,我们来看其处理请求的ProcessRequestInternal方法。

        private void ProcessRequestInternal(HttpWorkerRequest wr) {
            Interlocked.Increment(ref _activeRequestCount);
            if (_disposingHttpRuntime) {
                try {
                    wr.SendStatus(503, "Server Too Busy");
                    wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                    byte[] body = Encoding.ASCII.GetBytes("<html><body>Server Too Busy</body></html>");
                    wr.SendResponseFromMemory(body, body.Length);
                    wr.FlushResponse(true);
                    wr.EndOfRequest();
                } finally {
                    Interlocked.Decrement(ref _activeRequestCount);
                }
                return;
            }
            HttpContext context;
            try {
                context = new HttpContext(wr, false);
            }
            catch {
                try {
                    wr.SendStatus(400, "Bad Request");
                    wr.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentType, "text/html; charset=utf-8");
                    byte[] body = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");
                    wr.SendResponseFromMemory(body, body.Length);
                    wr.FlushResponse(true);
                    wr.EndOfRequest();
                    return;
                } finally {
                    Interlocked.Decrement(ref _activeRequestCount);
                }
            }
            wr.SetEndOfSendNotification(_asyncEndOfSendCallback, context);
            HostingEnvironment.IncrementBusyCount();
            try {
                try {
                    EnsureFirstRequestInit(context);
                }
                catch {
                    if (!context.Request.IsDebuggingRequest) {
                        throw;
                    }
                }
                context.Response.InitResponseWriter();
                IHttpHandler app = HttpApplicationFactory.GetApplicationInstance(context);
                if (app == null)
                    throw new HttpException(SR.GetString(SR.Unable_create_app_object));
                if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Verbose, EtwTraceFlags.Infrastructure)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, context.WorkerRequest, app.GetType().FullName, "Start");
                if (app is IHttpAsyncHandler) {
                    IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler)app;
                    context.AsyncAppHandler = asyncHandler;
                    asyncHandler.BeginProcessRequest(context, _handlerCompletionCallback, context);
                }
                else {
                    app.ProcessRequest(context);
                    FinishRequest(context.WorkerRequest, context, null);
                }
            }
            catch (Exception e) {
                context.Response.InitResponseWriter();
                FinishRequest(wr, context, e);
            }
        }

    该方法中创建了熟悉的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);
    
        }

    然后通过HttpApplicationFactory的GetApplicationInstance静态方法,获取我们熟悉的HttpApplication对象实例(注:HttpApplication对象是继承IHttpAsyncHandler,而IHttpAsyncHandler又继承于IHttpHandler),然后执行调用BeginProcessRequest方法。至此正式进入了HttpApplication对象的创建以及大家熟知的HttpApplication以后的生命周期了。

    HttpApplicationFactory分析

    HttpApplicationFactory在System.Web下实现。

    查看HttpApplicationFactory用来创建Httpapplication的GetApplicationInstance方法。

        internal static IHttpHandler GetApplicationInstance(HttpContext context) {
            if (_customApplication != null)
                return _customApplication;
            if (context.Request.IsDebuggingRequest)
                return new HttpDebugHandler();
            _theApplicationFactory.EnsureInited();
            _theApplicationFactory.EnsureAppStartCalled(context);
            return _theApplicationFactory.GetNormalApplicationInstance(context);
        }

    该方法有三个步骤:首先是EnsureInited,会检查是否已经初始化,如果没有会调用Init方法先获取global.asax文件的完整路径,然后调用CompileApplication()对global.asax进行编译,Init方法如下。

        private void Init() {
            if (_customApplication != null)
                return;
            try {
                try {
                    _appFilename = GetApplicationFile();
                    CompileApplication();
                 }
                finally {
                    SetupChangesMonitor();
                }
            }
            catch {
                throw;
            }
    }

    然后是EnsureAppStartCalled方法如果未开始启动会调用FireApplicationOnStart。

       private void EnsureAppStartCalled(HttpContext context) {
            if (!_appOnStartCalled) {
                lock (this) {
                    if (!_appOnStartCalled) {
                        using (new DisposableHttpContextWrapper(context)) {
                            WebBaseEvent.RaiseSystemEvent(this, WebEventCodes.ApplicationStart);
                            FireApplicationOnStart(context);
                            }
                        _appOnStartCalled = true;
                    }
                }
            }
        }
    
        private void FireApplicationOnStart(HttpContext context) {
            if (_onStartMethod != null) {
                HttpApplication app = GetSpecialApplicationInstance();
                app.ProcessSpecialRequest(context, _onStartMethod, _onStartParamCount, this, EventArgs.Empty, null);
                RecycleSpecialApplicationInstance(app);
            }
        }

    这里创建特定的HttpApplication实例,触发ApplicationOnStart事件(会执行global.asax中的Application_Start方法)。然后在处理完事件以后就立即被回收掉,因为系统初始化只需要一次。

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

        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) {
                app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
                using (new ApplicationImpersonationContext()) {
                    app.InitInternal(context, _state, _eventHandlerMethods);
                }
            }
            ……
            return app;
        }

    HttpApplication分析

    HttpApplication在System.Web下实现,首先查看HttpApplication的InitInternal方法,该方法用于初始化。

        internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) {
            Debug.Assert(context != null, "context != null");
            _state = state;
            PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES);
            try {
                try {
                    _initContext = context;
                    _initContext.ApplicationInstance = this;
                    context.ConfigurationPath = context.Request.ApplicationPathObject;
                    using (new DisposableHttpContextWrapper(context)) {
                        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();
                            Debug.Assert(null == _moduleContainers, "null == _moduleContainers");
                        }
                        if (handlers != null)
                            HookupEventHandlersForApplicationAndModules(handlers);
                        _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);
                    if (HttpRuntime.UseIntegratedPipeline) {
                        _stepManager = new PipelineStepManager(this);
                    }
                    else {
                        _stepManager = new ApplicationStepManager(this);
                    }
                    _stepManager.BuildSteps(_resumeStepsWaitCallback);
                }
                finally {
                    _initInternalCompleted = true;
                    context.ConfigurationPath = null;
                    _initContext.ApplicationInstance = null;
                    _initContext = null;
                }
            }
            catch {
                throw;
            }
        }

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

    总结一下,InitInternal方法的主要功能如下:

    InitModules():根据Web.Config的设置,加载相应的HttpModules。

    InitIntegratedModules():会加载IIS7集成模式下在服务器上设定的HttpModuels和Web.config里system.webserver下的HttpModuels。

    HookupEventHandlersForAppplicationAndModules:绑定HttpApplication实例中相应的事件处理函数(在Global.asax中定义的事件处理函数)。

    创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中(包括HttpModules中定义的周期事件处理函数和查找匹配的HttpHandler、执行HttpHandler的方法以及过滤输出等特殊事件),等待回调时执行。从这里我们可以看到HttpApplication是以异步的方式处理请求, 对请求的很多处理工作都放入了_execStep等待回调时执行。

    在 HttpApplication的事件如下形式定义:

        public event EventHandler BeginRequest {
            add { AddSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
            remove { RemoveSyncEventHookup(EventBeginRequest, value, RequestNotification.BeginRequest); }
        }

    所有的事件都是调用AddSyncEventHookup方法添加进去的,其中第一个参数是以Event+事件名称的值。

        internal void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification) {
            AddSyncEventHookup(key, handler, notification, false);
        }
    private void AddSyncEventHookup(object key, Delegate handler, RequestNotification notification, bool isPostNotification) { ThrowIfEventBindingDisallowed(); Events.AddHandler(key, handler); if (IsContainerInitalizationAllowed) { PipelineModuleStepContainer container = GetModuleContainer(CurrentModuleCollectionKey); if (container != null) { SyncEventExecutionStep step = new SyncEventExecutionStep(this, (EventHandler)handler); container.AddEvent(notification, isPostNotification, step); } } }

    经典模式下在初始化HttpModlue的时候通过调用Events.AddHandler方法,将事件添加到Events集合里,同时这个key就是Event+事件名称,而集成模式下这些事件是添加到另外一个地方的(通过将事件hanlder包装成SyncEventExecutionStep类型,然后调用container.AddEvent方法将事件添加到另外一个地方),也就是说if上面的Events集合是给经典模式用的,下面的Container里的数据是给集成模式用的,这些事件是存放在HttpApplication的ModuleContainers属性里,这个属性的类型是PipelineModuleStepContainer[],个数就是HttpModules的个数,也就是说每个HttpModule在HttpApplication上添加的事件都放在各自的PipelineModuleStepContainer容器里。

        private PipelineModuleStepContainer[] ModuleContainers {
            get {
                if (_moduleContainers == null) {
                    Debug.Assert(_moduleIndexMap != null && _moduleIndexMap.Count > 0, "_moduleIndexMap != null && _moduleIndexMap.Count > 0");
                    _moduleContainers = new PipelineModuleStepContainer[_moduleIndexMap.Count];
                    for (int i = 0; i < _moduleContainers.Length; i++) {
                        _moduleContainers[i] = new PipelineModuleStepContainer();
                    }
                }
                return _moduleContainers;
            }
    }

    StepManager分析

    集成模式和经典模式(或IIS6)使用的是不同的StepManager,这个类的BuildSteps方法就是为了创建有序的ExecutionStep,其中包括各种事件的事情以及其它在各时间周期之间穿插的操作,最主要的操作,大家以前就应该知道的,比如哪个周期可以判定使用哪个HttpHandler,以及在哪个周期内执行这个HttpHandler的BeginProcessRequest方法。StepManager的具体实现类(ApplicationStepManager、PipelineStepManager)和HttpApplication类在同一个文件中定义。

    ApplicationStepManager的BuildSteps方法(用于经典模式)

        internal override void BuildSteps(WaitCallback stepCallback ) {
            ArrayList steps = new ArrayList();
            HttpApplication app = _application;
            bool urlMappingsEnabled = false;
            UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
            urlMappingsEnabled = urlMappings.IsEnabled && ( urlMappings.UrlMappings.Count > 0 );
            steps.Add(new ValidateRequestExecutionStep(app));
            steps.Add(new ValidatePathExecutionStep(app));
            if (urlMappingsEnabled)
                steps.Add(new UrlMappingsExecutionStep(app)); // url mappings
            app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
            steps.Add(new MapHandlerExecutionStep(app));     // map handler
            app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
            steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());        
            steps.Add(new CallHandlerExecutionStep(app)); // execute handler       
            app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
            steps.Add(new CallFilterExecutionStep(app)); // filtering
            app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
            app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
            _endRequestStepIndex = steps.Count;
            app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
            steps.Add(new NoopExecutionStep()); // the last is always there
            _execSteps = new IExecutionStep[steps.Count];
            steps.CopyTo(_execSteps);
            _resumeStepsWaitCallback = stepCallback;
        }
    
        private void CreateEventExecutionSteps(Object eventIndex, ArrayList steps) {
            AsyncAppEventHandler asyncHandler = AsyncEvents[eventIndex];
            if (asyncHandler != null) {
                asyncHandler.CreateExecutionSteps(this, steps);
            }
            EventHandler handler = (EventHandler)Events[eventIndex];
            if (handler != null) {
                Delegate[] handlers = handler.GetInvocationList();
                for (int i = 0; i < handlers.Length; i++)  {
                    steps.Add(new SyncEventExecutionStep(this, (EventHandler)handlers[i]));
                }
             }
        }

    这个方法的完整功能归纳总结有以下几点:

    对请求的Request进行验证,ValidateRequestExecutionStep。

    对请求的路径进行安全检查,禁止非法路径访问(ValidatePathExecutionStep)。 

    如果设置了UrlMappings, 进行RewritePath(UrlMappingsExecutionStep)。

    执行事件处理函数,比如将BeginRequest、AuthenticateRequest转化成可执行ExecutionStep在正式调用时候执行。

    在这多个个事件操作处理期间,根据不同的时机加了4个特殊的ExecutionStep。

    MapHandlerExecutionStep:查找匹配的HttpHandler

    CallHandlerExecutionStep:执行HttpHandler的BeginProcessRequest

    CallFilterExecutionStep:调用Response.FilterOutput方法过滤输出

    NoopExecutionStep:空操作,留着以后扩展用

    所有的ExecuteionStep都保存在ApplicationStepManager实例下的私有字段_execSteps里,而HttpApplication的BeginProcessRequest方法最终会通过该实例的ResumeSteps方法来执行这些操作。

    PipelineStepManager的BuildSteps(用于集成模式)

        internal override void BuildSteps(WaitCallback stepCallback) {
            Debug.Trace("PipelineRuntime", "BuildSteps");
            HttpApplication app = _application;
            IExecutionStep materializeStep = new MaterializeHandlerExecutionStep(app);
            app.AddEventMapping(ttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.MapRequestHandler,
                        false, materializeStep);
            app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.ExecuteRequestHandler,
                        false, app.CreateImplicitAsyncPreloadExecutionStep());
            IExecutionStep handlerStep = new CallHandlerExecutionStep(app);
            app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.ExecuteRequestHandler,
                        false, handlerStep);
            IExecutionStep webSocketsStep = new TransitionToWebSocketsExecutionStep(app);
            app.AddEventMapping(HttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.EndRequest,
                        true, webSocketsStep);
            IExecutionStep filterStep = new CallFilterExecutionStep(app);
            app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
                        RequestNotification.UpdateRequestCache,
                        false, filterStep);
            app.AddEventMapping(HttpApplication.IMPLICIT_FILTER_MODULE,
                        RequestNotification.LogRequest,
                        false, filterStep);
            _resumeStepsWaitCallback = stepCallback;
        }
    
        private void AddEventMapping(string moduleName,RequestNotification requestNotification, bool isPostNotification,IExecutionStep step) {
            ......
            PipelineModuleStepContainer container = GetModuleContainer(moduleName);
            container.AddEvent(requestNotification, isPostNotification, step);
        }

    以上代码有2个地方和经典模式不相同:

    集成模式没有使用MapHandlerExecutionStep来装载ExecutionStep(也就是查找对应的HttpHandler),而是通过MaterializeHandlerExecutionStep类来获得HttpHandler,方式不一样。

    集成模式是通过HttpApplication的AddEventMapping方法来添加事件的,从而将事件加入到前面所说的ModuleContainers容器。

    总结一下,在经典模式下,是用 Event+事件名称做key将所有事件的保存在HttpApplication的Events属性对象里,然后在BuildSteps里统一按照顺序组装,中间加载4个特殊的ExecutionStep,最后在统一执行;在集成模式下,是通过HttpModule名称+RequestNotification枚举值作为key将所有的事件保存在HttpApplication的ModuleContainers属性对象里,然后也在BuildSteps里通过伪造HttpModule名称加载那4个特殊的ExecutionStep,最后按照枚举类型的顺序,遍历所有的HttpModule按顺序来执行这些事件,可以自行编写一个自定义的HttpModuel来执行这些事件看看效果如何。

    下面是总结一下处理第一次请求的大体处理流程。

  • 相关阅读:
    Windows Azure Web Site (19) Azure Web App链接到VSTS
    Windows Azure Virtual Machine (35) Azure VM通过Linked DB,执行SQL Job
    Azure PowerShell (16) 并行开关机Azure ARM VM
    Windows Azure Virtual Network (12) 虚拟网络之间点对点连接VNet Peering
    Azure ARM (21) Azure订阅的两种管理模式
    Windows Azure Platform Introduction (14) 申请海外的Windows Azure账户
    Azure ARM (20) 将非托管磁盘虚拟机(Unmanage Disk),迁移成托管磁盘虚拟机(Manage Disk)
    Azure ARM (19) 将传统的ASM VM迁移到ARM VM (2)
    Azure ARM (18) 将传统的ASM VM迁移到ARM VM (1)
    Azure Automation (6) 执行Azure SQL Job
  • 原文地址:https://www.cnblogs.com/ssxg/p/7085877.html
Copyright © 2011-2022 走看看