zoukankan      html  css  js  c++  java
  • ASP.NET 中 Session 实现原理浅析 [1] 会话的建立流程

        HTTP 协议之所以能够获得如此大的成功,其设计实现的简洁性和无状态连接的高效率是很重要的原因。而为了在无状态的 HTTP 请求和有状态的客户端操作之间达到平衡,产生了服务器端会话 (Session) 的概念。客户端在连接到服务器后,就由 Web 服务器产生并维护一个客户端的会话;当客户端通过无状态 HTTP 协议再次连接到服务器时,服务器根据客户端提交的某种凭据,如 Cookie 或 URL 参数,将客户关联到某个会话上。这种思路在各种开发语言和开发环境中大量得到应用。
        在 ASP.NET 中,Web 应用程序和会话状态被分别进行维护,通过 HttpApplication 和 HttpSessionState 分离 Web 应用程序与会话的功能。应用程序层逻辑在 Global.asax 文件中实现,运行时编译成 System.Web.HttpApplication 的实例;会话则作为单独的 System.Web.SessionState.HttpSessionState 实例,由服务器统一为每个用户会话维护,通过 ASP.NET 页面编译成的 System.Web.UI.Page 对象子类的 Session 属性访问。关于 ASP.NET 中不同层次关系可参考我以前的一篇文章《.NET 1.1中预编译ASP.NET页面实现原理浅析 [1] 自动预编译机制浅析》,以下简称【文1】。

        ASP.NET 在处理客户端请求时,首先将根据客户端环境,生成一个 System.Web.HttpContext 对象,并将此对象作为执行上下文传递给后面的页面执行代码。
        在【文1】的分析中我们可以看到,HttpRuntime 在处理页面请求之前,根据 HttpWorkerRequest 中给出的环境,构造 HttpContext 对象,并以次对象作为参数从应用程序池中获取可用应用程序。简要代码如下:
    private void HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr)
    {
      
    // 构造 HTTP 调用上下文对象
      HttpContext ctxt = new HttpContext(wr, 0);

      
    //

      
    // 获取当前 Web 应用程序实例
      IHttpHandler handler = HttpApplicationFactory.GetApplicationInstance(ctxt);

      
    // 调用 handler 实际处理页面请求
    }



        HttpApplicationFactory 工厂内部维护了一个可用的应用程序实例缓冲池,用户降低应用程序对象构造的负荷。
        如果池中没有可用的应用程序对象实例,此对象工厂最终会调用 System.Web.HttpRuntime.CreateNonPublicInstance 方法构造新的应用程序实例,并调用其 InitInternal 方法初始化。详细步骤分析见【文1】
    internal static IHttpHandler HttpApplicationFactory.GetApplicationInstance(HttpContext ctxt)
    {
      
    // 处理定制应用程序
      
    //

      
    // 处理调试请求
      
    //

      
    // 判断是否需要初始化当前 HttpApplicationFactory 实例
      
    //

      
    // 获取 Web 应用程序实例
      return HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(ctxt);
    }


    private HttpApplication HttpApplicationFactory.GetNormalApplicationInstance(HttpContext context)
    {
      HttpApplication app 
    = null;

      
    // 尝试从已施放的 Web 应用程序实例队列中获取
      
    //

      
    if(app == null)
      
    {
         
    // 构造新的 Web 应用程序实例
        app = (HttpApplication)System.Web.HttpRuntime.CreateNonPublicInstance(this._theApplicationType);

        
    // 初始化 Web 应用程序实例
        app.InitInternal(context, this._state, this._eventHandlerMethods);
      }


      
    return app;
    }


        这里的 System.Web.HttpApplication.InitInternal 函数完成对应用程序对象的初始化工作,包括调用 HttpApplication.InitModules 函数初始化 HTTP 模块(后面将详细介绍),并将作为参数传入的 HttpContext 实例保存到 HttpApplication._context 字段中。而此 HTTP 上下文对象将被后面用于获取会话对象。
    public class HttpApplication : 
    {
      
    private HttpContext _context;
      
    private HttpSessionState _session;

      
    public HttpSessionState Session
      
    {
        
    get
        
    {
          HttpSessionState state 
    = null;
          
    if (this._session != null)
          
    {
            state 
    = this._session;
          }

          
    else if (this._context != null)
          
    {
            state 
    = this._context.Session;
          }

          
    if (state == null)
          
    {
            
    throw new HttpException(HttpRuntime.FormatResourceString("Session_not_available"[img]/images/wink.gif[/img]);
          }

          
    return state;
        }

      }

    }


        而在 ASP.NET 页面中获取会话的方法也是类似,都是通过 HttpContext 来完成的。
    public class Page : 
    {
      
    private HttpSessionState _session;
      
    private bool _sessionRetrieved;

      
    public virtual HttpSessionState Session
      
    {
        
    get
        
    {
          
    if (!this._sessionRetrieved)
          
    {
            
    this._sessionRetrieved = true;
            
    try
            
    {
              
    this._session = this.Context.Session;
            }

            
    catch (Exception)
            
    {
            }

          }

          
    if (this._session == null)
          
    {
            
    throw new HttpException(HttpRuntime.FormatResourceString("Session_not_enabled"[img]/images/wink.gif[/img]);
          }

          
    return this._session;
        }

      }

    }


        在 HttpContext 中,实际上是通过一个哈希表保存诸如会话对象之类信息的
    public sealed class HttpContext : 
    {
      
    private Hashtable _items;

      
    public IDictionary Items
      
    {
        
    get
        
    {
          
    if (this._items == null)
          
    {
            
    this._items = new Hashtable();
          }

          
    return this._items;
        }

      }


      
    public HttpSessionState Session
      
    {
        
    get
        
    {
          
    return ((HttpSessionState) this.Items["AspSession"]);
        }

      }

    }


        而 HttpContext.Session 所访问的又是哪儿来的呢?这就又需要回到我们前面提及的 HttpApplication.InitModules 函数。

        在 .NET 安装目录 Config 子目录下的 machine.config 定义了全局性的配置信息,而 HttpApplication 就是使用其中 system.web 一节的配置信息进行初始化的。
    <system.web>
        
    <httpModules>
          
    <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
          
    <add name="Session" type="System.Web.SessionState.SessionStateModule" />
          
    <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
          
    <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
          
    <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
          
    <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
          
    <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
          
    <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        
    </httpModules>
    </system.web>

        httpModules 节点指定了 HttpApplication 需要初始化的模块列表,而在前面提到的 HttpApplication.InitModules 函数正式根据此列表进行初始化的
    private void HttpApplication.InitModules()
    {
      HttpModulesConfiguration cfgModules 
    = ((HttpModulesConfiguration) HttpContext.GetAppConfig("system.web/httpModules"[img]/images/wink.gif[/img]);

      
    if (cfgModules == null)
      
    {
        
    throw new HttpException(HttpRuntime.FormatResourceString("Missing_modules_config"[img]/images/wink.gif[/img]);
      }

      _moduleCollection 
    = cfgModules.CreateModules();

      
    for(int i = 0; i < _moduleCollection.Count; i++)
      
    {
        _moduleCollection[i].Init(
    this);
      }


      GlobalizationConfig cfgGlobal 
    = ((GlobalizationConfig) HttpContext.GetAppConfig("system.web/globalization"[img]/images/wink.gif[/img]);
      
    if (cfgGlobal != null)
      
    {
        _appLevelCulture 
    = cfgGlobal.Culture;
        _appLevelUICulture 
    = cfgGlobal.UICulture;
      }

    }


        Session 节点对于的 System.Web.SessionState.SessionStateModule 对象将被 HttpModulesConfiguration.CreateModules 方法构造,并调用其 Init 函数初始化。SessionStateModule 类实际上就是负责管理并创建会话,用户完全可以自行创建一个实现 IHttpModule 接口的类,实现会话的控制,如实现支持集群的状态同步等等。
        SessionStateModule.Init 方法主要负责 machine.config 文件中的 sessionState 配置,调用 SessionStateModule.InitModuleFromConfig 方法建立相应的会话管理器。
    <system.web>
        
    <sessionState mode="InProc"
                      stateConnectionString
    ="tcpip=127.0.0.1:42424"
                      stateNetworkTimeout
    ="10"
                      sqlConnectionString
    ="data source=127.0.0.1;Integrated Security=SSPI"
                      cookieless
    ="false"
                      timeout
    ="20" />
    </system.web>

        sessionState 的使用方法请参加 MSDN 中相关介绍和 INFO: ASP.NET State Management Overview。关于不同 mode 的会话管理的话题我们后面再讨论,先继续来看会话的建立过程。

        在从 machine.config 文件中读取配置信息后,InitModuleFromConfig 方法会向 HttpApplication 实例注册几个会话管理事件处理函数,负责在应用程序合适的情况下维护会话状态。
    private void SessionStateModule.InitModuleFromConfig(HttpApplication app,
        SessionStateSectionHandler.Config config, 
    bool configInit)
    {
      
    // 处理不使用 Cookie 的情况
      
    //

      app.AddOnAcquireRequestStateAsync(
    new BeginEventHandler(this.BeginAcquireState),
                                        
    new EndEventHandler(this.EndAcquireState));
      app.ReleaseRequestState 
    += new EventHandler(this.OnReleaseState);
      app.EndRequest 
    += new EventHandler(this.OnEndRequest);

      
    // 创建会话管理器
      
    //
    }


        BeginAcquireState 和 EndAcquireState 作为一个异步处理器注册到 HttpApplication._acquireRequestStateEventHandlerAsync 字段上;OnReleaseState 则负责在合适的时候清理会话状态;OnEndRequest 则是 OnReleaseState 的一个包装,负责较为复杂的请求结束处理。前面提到的 HttpApplication.InitInternal 函数,在完成了初始化工作后,会将上述这些事件处理器,加入到一个执行队列中,由应用程序在合适的时候,使用流水线机制进行调用,最大化处理效率。有关 ASP.NET 中流水线事件模型的相关介绍,请参考 HTTP PIPELINES
    Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET
     一文中 The Pipeline Event Model 小节,有机会我在写文章详细分析。

        知道了会话建立的调用流程再来看会话的实现就比较简单了,SessionStateModule.BeginAcquireState 被 HttpApplication 实例在合适的时候调用,处理各种会话的复杂情况后,使用 SessionStateModule.CompleteAcquireState 函数完成实际的会话建立工作,并将封装会话的 HttpSessionState 对象以 "AspSession" 为 key 加入到 HttpContext 的哈希表中,也就是前面提到的 HttpContext.Context 的由来。而 SessionStateModule.OnReleaseState 则从 HttpContext 中删除 "AspSession" 为 key 的 HttpSessionState 对象,并对会话管理器进行同步工作。

        至此,ASP.NET 中的会话建立流程大概就分析完毕了,下一小节将进一步展开分析多种不同会话管理器的实现原理与应用。
  • 相关阅读:
    大数据组件原理总结-Hadoop、Hbase、Kafka、Zookeeper、Spark
    淘宝搜索引擎的缓存机制入门总结
    Log4j写日志文件使用详解
    storm入门(一):storm编程框架与举例
    storm入门(二):关于storm中某一段时间内topN的计算入门
    关于京东推荐模型的阅读理解
    运维开发入门记录
    Redis 3.0.0 集群部署
    Redis集群部署
    秘籍
  • 原文地址:https://www.cnblogs.com/dagon007/p/124140.html
Copyright © 2011-2022 走看看