• Asp.Net构架(Http请求处理流程)


    Http请求处理流程概述

    当用户在浏览器输入一个URL地址后,浏览器会发送一个请求到服务器。这时候在服务器上第一个负责处理请求的是IIS。然后IIS再根据请求的URL扩展名将请求分发给不同的ISAPI处理,然后,IIS 接收返回的数据流,并重新返还给 HTTP.SYS,最后,HTTP.SYS 再将这些数据返回给客户端浏览器。

    ISAPI

    ISAPI是一个底层的WIN32 API,开发者可以使用这些接口深入到IIS,让IIS支持各种其他处理程序。ISAPI是一个桥接口,通常用于高层次的工具与IIS之间的接驳。例如Windows下的Apache与Tomcat就是构建于ISAPI之上。ISAPI是自定义Web请求处理中第一个IIS入口点。

    能够处理各种后缀名的应用程序,通常被称为ISAPI应用程序(NOTE:Internet Server Application Programe Interface,互联网服务器应用程序接口)。虽然这 ISAPI 听上去还挺气派,也算是“应用程序”呢,但仔细看看它的全称就明白了:它实际上只是一个接口,起到一个代理的作用,它的主要工作是映射所请求的页面(文件) 和与此后缀名相对应的实际的处理程序

    Http请求刚刚到达服务器的时候

    IIS依赖一个叫做 HTTP.SYS 的内置驱动程序来监听来自外部的 HTTP请求。在操作系统启动的时候,IIS首先在HTTP.SYS中注册自己的虚拟路径。

    实际上相当于告诉HTTP.SYS哪些URL是可以访问的,哪些是不可以访问的。为什么你访问不存在的文件会出现 404 错误呢?就是在这一步确定的。

    如果请求的是一个可访问的URL,HTTP.SYS会将这个请求交给 IIS 工作者进程。

    当服务器接收到一个 Http请求的时候,IIS 如何去处理这个请求,依据什么去处理呢?―― 根据文件的后缀名。对于html页面,txt文件,jpeg和gif图像的请求,IIS就自己处理了,当发现请求是Asp.net的资源时(如.aspx,.asmx,*.ashx),请求将传递到ASP.NET ISAPI扩展aspnet_isapi.dll。

    服务器获取所请求的页面的后缀名以后,接下来会在服务器端寻找可以处理这类后缀名的应用程序,如果IIS找不到可以处理此类文件的应用程序,并且这个文件也没有受到服务器端的保护(NOTE:一个受保护的例子就是 cs文件,一个不受保护的例子就是js脚本),那么IIS将直接把这个文件返还给客户端。

    aspnet_isapi.dll可以处理多种资源类型,包括Web服务和HTTP处理程序调用等。

    除了以外,ISAPI 还做一些其他的工作:

    • 从HTTP.SYS中获取当前的Httq请求信息,并且将这些信息保存到 HttpWorkerRequest 类中。
    • 在相互隔离的应用程序域AppDomain中加载HttpRuntime。
    • 调用 HttpRuntime的ProcessRequest方法。
    IIS与aspnet_isapi的处理逻辑:
    • 当请求到达时,IIS检查资源类型并调用aspnet_isapi扩展。如果启用了默认的进程模型。aspnet_isapi会将请求排队。并将请求分配给辅助进程、所有的请求数据都是通过异步I/O发送。
    • 收到请求后,ASP.NET辅助进程将通知aspnet_isapi,它将为请求服务(建立管道)。
    • 在辅助进程的上下文中执行请求。有时,辅助进程可能需要回调aspnet_isapi以完成请求,也就是需要说枚举服务器变量。这种情况下,辅助进程将使用同步管道,因为这样可以保持请求处理的逻辑顺序。
    • 完成后,响应被发送到打开了异步管道的aspnet_isapi。如果aspnet_isapi检测到辅助进程已取消,它将自动终止请求并释放所有相关的IIS资源。

    宿主环境(Hosting)与管道(Pipeline)

    从本质上讲,Asp.Net 主要是由一系列的类组成,这些类的主要目的就是将Http请求转变为对客户端的响应。

    当 Web.config文件的内容发生改变 或者 .aspx文件发生变动的时候,为了能够卸载运行在同一个进程中的应用程序(NOTE:卸载也是为了重新加载),Http请求被分放在相互隔离的应用程序域中(应用程序域就是 AppDomain)。

    • Asp.net处理管道的第一步是创建HttpWorkerRequest对象,它包含于当前请求有关的所有信息。
    • HttpWorkerRequest把请求传递给HttpRuntime类的静态ProcessRequest方法。HttpRuntime首先要做的事是创建HttpContext对象,并用HttpWorkerRequest进行初始化。
            /// <devdoc>
            ///    <para><SPAN>The method that drives
            ///       all ASP.NET web processing execution.</SPAN></para>
            /// </devdoc>
            [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Medium)]
            public static void ProcessRequest(HttpWorkerRequest wr) {
                if (wr == null)
                    throw new ArgumentNullException("wr");
    
                if (HttpRuntime.UseIntegratedPipeline) {//判断是否为集成管道
                    throw new PlatformNotSupportedException(SR.GetString(SR.Method_Not_Supported_By_Iis_Integrated_Mode, "HttpRuntime.ProcessRequest"));
                }
    
                ProcessRequestNoDemand(wr);
            }
            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);
                }
            }
            private void ProcessRequestInternal(HttpWorkerRequest wr) {
                // Count active requests
                Interlocked.Increment(ref _activeRequestCount);
    
                if (_disposingHttpRuntime) {//当请求数量超出程序处理速度,服务器返回503,服务器繁忙的响应
                    // Dev11 333176: An appdomain is unloaded before all requests are served, resulting in System.AppDomainUnloadedException during isapi completion callback
                    //
                    // HttpRuntim.Dispose could have already finished on a different thread when we had no active requests
                    // In this case we are about to start or already started unloading the appdomain so we will reject the request the safest way possible
                    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);
                        // this will flush synchronously because of HttpRuntime.ShutdownInProgress
                        wr.FlushResponse(true);
                        wr.EndOfRequest();
                    } finally {
                        Interlocked.Decrement(ref _activeRequestCount);
                    }
                    return;
                }
    
                // Construct the Context on HttpWorkerRequest, hook everything together
                HttpContext context;
    
                try {
                    context = new HttpContext(wr, false /* initResponseWriter */);
                } 
                catch {//HttpContext 组装错误,返回400错误
                    try {
                        // If we fail to create the context for any reason, send back a 400 to make sure
                        // the request is correctly closed (relates to VSUQFE3962)
                        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 {
                    // First request initialization
                    try {
                        EnsureFirstRequestInit(context);
                    }
                    catch {
                        // If we are handling a DEBUG request, ignore the FirstRequestInit exception.
                        // This allows the HttpDebugHandler to execute, and lets the debugger attach to
                        // the process (VSWhidbey 358135)
                        if (!context.Request.IsDebuggingRequest) {
                            throw;
                        }
                    }
    
                    // Init response writer (after we have config in first request init)
                    // no need for impersonation as it is handled in config system
                    context.Response.InitResponseWriter();
    
                    // Get application instance
                    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) {
                        // 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) {
                    context.Response.InitResponseWriter();
                    FinishRequest(wr, context, e);
                }
            }
    
    
    • 当Http请求进入 Asp.Net Runtime以后,它的管道由托管模块(NOTE:Managed Modules)和处理程序(NOTE:Handlers)组成,并且由管道来处理这个 Http请求。
    • 创建了HttpContext实例之后,HttpRuntime类就通过调用HttpApplicationFactory的静态GetApplicationInstance()方法,为该应用程序请求HttpApplication派生类的一个示例。GetApplicationInstance()方法要么创建一个HttpApplication类的一个新实例,要么从应用程序对象池中取出一个实例。
            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);
            }
            private void EnsureInited() {
                if (!_inited) {
                    lock (this) {
                        if (!_inited) {
                            Init();
                            _inited = true;
                        }
                    }
                }
            }
            //
            // Initialization on first request
            //
    
            private void Init() {
                if (_customApplication != null)
                    return;
    
                try {
                    try {
                        _appFilename = GetApplicationFile();//获取global.asax 地址
    
                        CompileApplication();//编译应用程序
                    }
                    finally {
                        // Always set up global.asax file change notification, even if compilation
                        // failed.  This way, if the problem is fixed, the appdomain will be restarted.
                        SetupChangesMonitor();//启动变化监控
                    }
                }
                catch { // Protect against exception filters
                    throw;
                }
            }
    
            //
            //  Application instance management
            //
    
            private HttpApplication GetNormalApplicationInstance(HttpContext context) {
                HttpApplication app = null;
    
                if (!_freeList.TryTake(out app)) {
                    // If ran out of instances, create a new one
                    app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
    
                    using (new ApplicationImpersonationContext()) {
                        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实例之后,就对它进行初始化,并在初始化期间分配应用程序定义的所
      有模块。模块式实现IHttpModule接口的类,作用就是为了实现那经典的19个标准处理事件。
            private void CompileApplication() {
                // Get the Application Type and AppState from the global file
    
                _theApplicationType = BuildManager.GetGlobalAsaxType();
    
                BuildResultCompiledGlobalAsaxType result = BuildManager.GetGlobalAsaxBuildResult();
    
                if (result != null) {
    
                    // Even if global.asax was already compiled, we need to get the collections
                    // of application and session objects, since they are not persisted when
                    // global.asax is compiled.  Ideally, they would be, but since <object> tags
                    // are only there for ASP compat, it's not worth the trouble.
                    // Note that we only do this is the rare case where we know global.asax contains
                    // <object> tags, to avoid always paying the price (VSWhidbey 453101)
                    if (result.HasAppOrSessionObjects) {
                        GetAppStateByParsingGlobalAsax();
                    }
    
                    // Remember file dependencies
                    _fileDependencies = result.VirtualPathDependencies;
                }
    
                if (_state == null) {
                    _state = new HttpApplicationState();
                }
    
    
                // Prepare to hookup event handlers via reflection
    
                ReflectOnApplicationType();
            }
            private void ReflectOnApplicationType() {//这个方法为应用程序注册事件
                ArrayList handlers = new ArrayList();
                MethodInfo[] methods;
    
                Debug.Trace("PipelineRuntime", "ReflectOnApplicationType");
    
                // 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);
                }
                
                // get base class private methods (GetMethods would not return those)
                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);
                    }
                }
    
                // remember as an array
                _eventHandlerMethods = new MethodInfo[handlers.Count];
                for (int i = 0; i < _eventHandlerMethods.Length; i++)
                    _eventHandlerMethods[i] = (MethodInfo)handlers[i];
            }
            private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) {
                if (m.ReturnType != typeof(void))
                    return false;
    
                // has to have either no args or two args (object, eventargs)
                ParameterInfo[] parameters = m.GetParameters();
    
                switch (parameters.Length) {
                    case 0:
                        // ok
                        break;
                    case 2:
                        // param 0 must be object
                        if (parameters[0].ParameterType != typeof(System.Object))
                            return false;
                        // param 1 must be eventargs
                        if (parameters[1].ParameterType != typeof(System.EventArgs) &&
                            !parameters[1].ParameterType.IsSubclassOf(typeof(System.EventArgs)))
                            return false;
                        // ok
                        break;
    
                    default:
                        return false;
                }
    
                // check the name (has to have _ not as first or last char)
                String name = m.Name;
                int j = name.IndexOf('_');
                if (j <= 0 || j > name.Length-1)
                    return false;
    
                // special pseudo-events
                if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") ||
                    StringUtil.EqualsIgnoreCase(name, "Application_Start")) {
                    _onStartMethod = m;
                    _onStartParamCount = parameters.Length;
                }
                else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") ||
                         StringUtil.EqualsIgnoreCase(name, "Application_End")) {
                    _onEndMethod = m;
                    _onEndParamCount = parameters.Length;
                }
                else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") ||
                         StringUtil.EqualsIgnoreCase(name, "Session_End")) {
                    _sessionOnEndMethod = m;
                    _sessionOnEndParamCount = parameters.Length;
                }
    
                return true;
            }
            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;
                        }
                    }
                }
            }
            //
            //  Application on_start 
            //
    
            private void FireApplicationOnStart(HttpContext context) {
                if (_onStartMethod != null) {
                    HttpApplication app = GetSpecialApplicationInstance();
    
                    app.ProcessSpecialRequest(
                                             context,
                                             _onStartMethod,
                                             _onStartParamCount,
                                             this, 
                                             EventArgs.Empty, 
                                             null);//执行前面注册的启动事件_onStartMethod
    
                    RecycleSpecialApplicationInstance(app);
                }
            }
            private HttpApplication GetSpecialApplicationInstance() {
                return GetSpecialApplicationInstance(IntPtr.Zero, null);
            }
            private HttpApplication GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) {
                HttpApplication app = null;
    
                if (!_specialFreeList.TryTake(out app)) {
                    //
                    //  Put the context on the thread, to make it available to anyone calling
                    //  HttpContext.Current from the HttpApplication constructor or module Init
                    //
                    using (new DisposableHttpContextWrapper(context)) {
                        // If ran out of instances, create a new one
                        app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType);
    
                        using (new ApplicationImpersonationContext()) {
                            app.InitSpecial(_state, _eventHandlerMethods, appContext, context);
                        }
                    }
                }
    
                return app;
            }
    
    
    • 在创建了模块之后,HttpRuntime类通过调用它的BeginProcessRequest方法,要求最新检索到的HttpApplication类对当前请求提供服务。然后,为当前请求找到合适的处理程序工厂。
      创建处理程序,传递当前HttpContext,一旦ProcessRequest方法返回,请求完成。
    本文参考文档
  • 相关阅读:
    165. Compare Version Numbers
    164. Maximum Gap
    3、桶排序
    162. Find Peak Element
    160. Intersection of Two Linked Lists
    155. Min Stack
    154. Find Minimum in Rotated Sorted Array II
    153. Find Minimum in Rotated Sorted Array
    Linux/Unix系统编程手册 第二章:基本概念
    Linux/Unix系统编程手册 第一章:历史和标准
  • 原文地址:https://www.cnblogs.com/Dewumu/p/12988276.html
Copyright © 2020-2023  润新知