纠结的Session备忘

使用中的问题: 

一个Asp.net的CRM项目在Session中存储自定义类型(可序列化的),开始使用的是InProc方式,几个月过去了一切都很和谐,但是最近随着使用人数的增加进程内Session经常丢失,于是业务员就很郁闷,好在MS提供了SqlServer与StateServer,这样可以在数据库或状态服务中保存Session,我只化了3分时间就把Web.config中的配置调整成StateServer方式,本以为这样就天下太平了,结果发现我错了,系统运行一段时间(或者说一个用户登录后点个7,8下)就会报错,错误点是SessionStateItemCollection.Deserialize。再接下来的时间里我想了各种办法来解决这个问题,我甚至将自定义类型,先使用xml序列化成字符串后再保存到Session里这样Session里保存的就是简单的string类型了,结果问题依旧,最后试了SqlServer方式,依然失败。 现阶段是将一些短数据放到cookie里,其他长数据在使用时从数据库中加载,好在当时对存储在Session里的数据存取进行了简单的封装,页面里没有直接使用Session["xx"]=yy; 这样的代码,不然项目里200多个并且出自4个程序员之手的页面改起来够崩溃了。

程序里主要代码如下:

public class UserSetting{

  public static T LoadSession<T>(string key) where T:class{

    return HttpContext.Current.Session[key] as T;

   }

     public static void SaveSession<T>(string key, T data)where T:class
      {
            HttpContext.Current.Session[key] = data;

       }

    //........其他属性

}

保存在Session里的自定义类型如下:

纠结的Session备忘

ISerializable的代码,多是基本类型

       #region ISerializable 成员

       public void GetObjectData(SerializationInfo info, StreamingContext context)
       {
           info.AddValue("Code",Code);
           info.AddValue("CurrentAuthType", CurrentAuthType);
           info.AddValue("IsRegionalManager", IsRegionalManager);
           info.AddValue("IsSalesman", IsSalesman);
           info.AddValue("AreaName", AreaName);
           info.AddValue("EmployeId", EmployeId);
           info.AddValue("AreaId", AreaId);
           info.AddValue("CurrentRole", CurrentRole);
           info.AddValue("Name", Name);
       }
       private UserSessionData(SerializationInfo info, StreamingContext context)
       {
           Code = info.GetString("Code");
           CurrentAuthType = info.GetInt32("CurrentAuthType");
           IsRegionalManager = info.GetBoolean("IsRegionalManager");
           IsSalesman = info.GetBoolean("IsSalesman");
           AreaName = info.GetString("AreaName");
           EmployeId = info.GetInt32("EmployeId");
           AreaId = info.GetInt32("AreaId");
           CurrentRole = info.GetString("CurrentRole");
           Name = info.GetString("Name");
       }

       #endregion

进程内Session运行正常,stateSever,跟SqlServer Session运行总会出现如下错误,

纠结的Session备忘

纠结的Session备忘

寻思了一阵子还是解决不了,目前打算自己实现个简单的Session,于是就开始了解asp.net Session的实现方式

Session机制的基本描述

Http是无状态的,Asp.net 会在用户访问具体.aspx页面时写个Asp.net_SessionId:xxxxxxxxxxx的cookie到客户端的浏览器中(也可以通过url方式),这样客户浏览器以后发起的每个请求都会带上这个cookie数据(可以通过火狐的fireBug中的网络观察到),asp.net利用从Asp.net_SessionId字段中获取的数据,也就是"xxxxxxxxxxx"到Session数据存储区(InProc,SqlServer,StateServer)检索对应的数据,然后构造出Session相关对象,供客户程序访问,具体一点的说就是通过SessionStateModule注册HttpApplication生命周期中的AcquireRequestState与ReleaseRequestState事件,分别将数据存储区的数据读出反序列化后附加到访问HttContext.Session上,以及将HttContext.Session数据序列化后保存到数据存储区中,而对数据存储区的读写由实现了SessionStateStoreProviderBase的类完成(Asp.net的提供程序模型)。

涉及的主要类

纠结的Session备忘

Session工作的基本流程

注意:这个流程图我参考相关资料大致画的,打算自己按这个方式实现一个MySession,不是微软Session的完整流程,MS的那个代码涉及面太多了,本人水平有限没完整理清。

纠结的Session备忘

纠结的Session备忘

上图中有个IRequiresSessionState类型是一个标记接口,用来告诉SessionStateModule是否要给当前请求构建立Session对象,一般站点上对.jpg,.png.html等资源的请求是不会发给asp.net来处理的,但是如果你配置了IIS,或者采用MVC建立应用程序时,对这些资源的请求都会发给asp.net的,如果跟踪请求会发现有大量图片的页面在打开时会有大量的ResetTimout操作出现,影响性能,这个时候可以将具体的资源目录(如/images)转化成虚拟目录,并取消静态资源到asp.net处理程序的映射

纠结的Session备忘

Session的锁定机制

Session数据是一个浏览器一份的(默认采用Httponley cookie来保存SessionId)数据间各自独立,但是如果网站中使用框架,或ajax中并行发起请求,就可能出现Session访问并发问题,Asp.net采用的机制是针对两个并发的请求(携带同一个SessionID),asp.net会根据SessionID去锁定Session数据存储中的对应记录,直到HttpAplication的ReleaseRequestState阶段释放锁定,而这个时候另外一个并非请求会被阻塞,并且每隔半秒再尝试一次直到超时或锁定成功。

Asp.net程序中,一个请求会由一个HttpApplication类应答,一个HttpApplication同一时间只处理一个请求,但是会有多个HttpApplication存在,以提供对网站并发访问请求的处理,HttpApplication是可以重复利用的,每个HttpApplication中都有一组独立的HttpModule,HttpApplication对象在被第一次创建时会根据配置建立自己的HttpMoudle集合,并调用每个HttpModule的Init方法,而每个HttpApplication再次被使用时(除创建那次外),不会再次构建HttpModule集合,当然也不会调用注册的HttpModule的Init方法了,这点在实现SessionStateModule时需要注意,内存结构参考下图:

纠结的Session备忘

Session的序列化

Session中的数据采用BinaryWriter,BinaryReader进行读写,具体在声明成internal的System.Web.Util.AltSerialization中实现,
Session数据最终按: [数据类型编号1][数据长度][数据][数据类型编号2][数据]...这样的方式存在成二进制格式,需要注意的是当[数据类型编号]是int,long这些长度已知的数据类型时[数据长度]是不写入的,另外自定义类型由BinaryFormatter类对流进行读写,代码截图如下:

纠结的Session备忘

一些参考网地址:

ASP.NET 2.0 异步页面原理浅析 [1]

如何:演示会话状态存储提供程序

你可能感兴趣的:(session)