状态管理本来是一件很美好的事情,嘿嘿,只可惜总是有些厂商在实现的时候考虑得不那么周全。例如MS在ASP中的状态管理实现就比较烂,因为只实现了一个进程内的基于内存的状态管理,故而存在很多问题:
1.所有的Session数据都保存在Web服务的进程中,会造成服务器支持会话数量受到服务器内存资源的限制问题,同时也因为大量非活动会话导致内存被无效占用。
2.服务器进程崩溃会导致所有的会话数据丢失。
3.会话无法跨进程或在负载均衡情况下使用,除非负载均衡技术保障同一用户每次都能被路由到同一机器上。就算这样也无法保障服务器崩溃造成的会话数据丢失。
4.需要Cookie的支持,而现在因为安全性问题,很多人在浏览器中关闭了Cookie和js的支持。
为此ASP的使用者不得不自己手工将会话信息以会话ID为主键同步到外部数据库中,以缓解类似问题。
而在ASP.NET中,因为设计时就考虑了这些问题,能够避免这些限制:
1.支持进程外的状态管理,通过独立状态管理服务或SQLServer状态服务器管理会话状态
2.支持不使用Cookie的状态维护,通过在URL中自动增加会话ID来避免使用Cookie
3.通过独立的状态管理服务或SQLServer状态服务器支持负载均衡时同步使用会话信息
实现这些特性的正是上节提到的
SessionStateModule.InitModuleFromConfig函数中,根据
sessionState标记的mode属性选择的四种不同的状态管理器实现。
<system.web> <sessionStatemode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" stateNetworkTimeout="10" sqlConnectionString="datasource=127.0.0.1;IntegratedSecurity=SSPI" cookieless="false" timeout="20"/> </system.web>
|
|
Off模式禁止会话管理,同时ASP.NET还允许通过在页面中以EnableSessionState属性细粒度管理页面的会话支持状态
<%@PageEnableSessionState="True|False|ReadOnly"%>
|
|
InProc模式兼容以前ASP的策略,在ASP.NET同一进程空间内实现基于内存的会话状态管理,速度最快但受到与ASP相同的限制;
StateServer模式通过ASP.NET独立安装的ASP.NETStateService服务(aspnet_state.exe),以stateConnectionString指定的IP和端口响应会话状态服务;
SQLServer模式则通过sqlConnectionString指定的SQLServer服务器,以内存临时表(以InstallSqlState.sql建库,使用tempdb内存数据库)或独立表(以InstallPersistSqlState.sql监控,使用独立的ASPState库)维护会话状态。
这四种不同的状态管理器,在性能上据《PerformanceTuningandOptimizingASP.NETAppliation》一书的测试,相对值如下:
以下为引用:
Table4-1:NormalizedTTLB(TimetoLastByte)bySessionStateMode(inMillisecondsper100Requests)
CONCURRENTBROWSERSMODE=OFFMODE=INPROCMODE=STATESERVERMODE=SQLSERVER 17.814.548.278.47 528.2820.2527.2529.29 1089.3846.0877.2985.11
Table4-2:AverageRequestsperSecondbySessionStateMode
CONCURRENTBROWSERSMODE=OFFMODE=INPROCMODE=STATESERVERMODE=SQLSERVER
118.8624.1718.3118.11 521.6625.7421.5421.34 1017.2323.818.1117.6
|
可以看到,无论是从TTLB还是每秒平均请求数来说,进程外状态管理器的性能都是可以令人接受的,当然还需要针对状态管理情况在编写代码时做相关优化。不过要使用进程外状态管理器,则保存在会话中的对象受到必须提高二进制序列化支持的限制。
从使用角度来看,状态管理器实际上都是由上节提到的HttpSessionModule建立管理,并通过HttpSessionState接口提供访问的,结构如下图:
MSDN上的
UnderpinningsoftheSessionStateImplementationinASP.NET一文非常详细的解释了几种不同状态管理器的原理和使用,这儿就不罗嗦了。
从实现角度来看,上节中提到的
SessionStateModule.InitModuleFromConfig函数,根据配置文件中状态管理器的模式,分别建立System.Web.SessionState.InProcStateClientManager,System.Web.SessionState.OutOfProcStateClientManager和System.Web.SessionState.SqlStateClientManager三类状态管理器的实例。他们都继承自System.Web.SessionState.StateClientManager抽象基类,并通过System.Web.SessionState.IStateClientManager接口向HttpApplication提高状态管理服务。
IStateClientManager接口是状态管理器的统一管理接口,主要提供以下功能:
internalinterfaceSystem.Web.SessionState.IStateClientManager.IStateClientManager { //配置管理状态管理器 voidConfigInit(SessionStateSectionHandler.Configconfig,SessionOnEndTargetonEndTarget); //保存SessionStateModule实例供后面使用 voidSetStateModule(SessionStateModulemodule); voidResetTimeout(stringid); voidDispose();
voidSet(stringid,SessionStateItemitem,boolinStorage);
//维护状态管理器内容 IAsyncResultBeginGet(stringid,AsyncCallbackcb,objectstate); SessionStateItemEndGet(IAsyncResultar);
IAsyncResultBeginGetExclusive(stringid,AsyncCallbackcb,objectstate); SessionStateItemEndGetExclusive(IAsyncResultar); voidReleaseExclusive(stringid,intlockCookie); }
|
|
ConfigInit方法主要在初始化状态管理器时通知其根据配置进行初始化工作,并将负责会话状态清除的SessionOnEndTarget对象实例绑定到会话管理器(我们后面讨论会话状态管理实现时详细讨论)。对OutOfProcStateClientManager和SqlStateClientManager来说,在此阶段还会初始化与外部服务器的连接,并通过一个System.Web.Util.ResourcePool实例,提供基于时间策略的资源池来维护连接;
ResetTimeout方法重置指定Session的超时时间;对InProcStateClientManager来说,这个超时时间是通过System.Web.Caching.CacheInternal类型实现的缓存对象来使用的;OutOfProcStateClientManager直接通过MakeRequest函数构造请求发给外部独立的状态管理器执行;SqlStateClientManager则调用存储过程TempResetTimeout更新ASPStateTempSessions表的过期时间Expires字段;
Dispose方法是否状态管理器的资源,落实到代码就是对OutOfProcStateClientManager和SqlStateClientManager中资源池的释放;
Set方法则将指定的SessionStateItem存储到id相关的会话数据中,并根据inStorage指定的对象状态,决定在发生异常的情况下是否释放对此会话的锁。与ResetTimeout的实现类似,OutOfProcStateClientManager发送请求给外部独立的状态管理器;SqlStateClientManager调用存储过程TempUpdateStateItemXXX更新会话状态表ASPStateTempSessions中的过期时间Expires字段、锁定状态Lock字段、以及状态信息SessionItemShort/SessionItemLong(分别保存7000字节以下或之上的数据)。如发生异常并设置inStorage标记,则先调用TempReleaseStateItemExclusive释放会话锁。
对状态管理器中数据的获取较为复杂,IStateClientManager接口使用的是异步调用的模式,并为提高效率将独占的获取数据单独拿出来。状态管理器实现类通过通用基类System.Web.SessionState.StateClientManager实现的几个工具方法,将数据获取操作异步化。再最终由实现类通过Get和GetExclusive方法完成操作。获取数据的方法InProcStateClientManager通过缓存;OutOfProcStateClientManager通过请求;SqlStateClientManager通过TempGetStateItemXXX存储过程完成。
在了解了
SessionStateModule控制的状态服务器的实现和使用方法后,我们来看看上层的HttpSessionState是如何使用的。
MandeepSBhatia的
ASP.NETSessionManagementInternals介绍了HttpSessionState内部完成状态信息管理的原理。HttpSessionState的Item属性实际上是通过SessionDictionary实例实现的。
publicsealedclassHttpSessionState: { privateSessionDictionary_dict;
publicobjectthis[stringname] { get { return_dict[name]; } set { _dict[name]=value; } } }
|
|
而此SessionDictionary实例与HttpSessionState实例的构造,都是在前面提到的完成会话构造的
SessionStateModule.CompleteAcquireState方法中完成的:
publicsealedclassSessionStateModule:IHttpModule { privatestring_rqId; privateSessionDictionary_rqDict; privateHttpStaticObjectsCollection_rqStaticObjects;//静态对象,通过页面中<objectRunat="Server"Scope="Session"/>标记设置 privateint_rqTimeout; privatebool_rqIsNewSession; privatebool_rqReadonly; privateHttpContext_rqContext; privateSessionStateItem_rqItem;
privatevoidCompleteAcquireState() { if(_rqItem!=null) { if(_rqItem.dict!=null) { _rqDict=_rqItem.dict; } else { _rqDict=newSessionDictionary(); } _rqStaticObjects=((_rqItem.staticObjects!=null)?_rqItem.staticObjects: _rqContext.Application.SessionStaticObjects.Clone()); _rqTimeout=_rqItem.timeout; _rqIsNewSession=false; _rqInStorage=true; _rqStreamLength=_rqItem.streamLength; } else { _rqDict=newSessionDictionary(); _rqStaticObjects=_rqContext.Application.SessionStaticObjects.Clone(); _rqTimeout=SessionStateModule.s_config._timeout; _rqIsNewSession=true; _rqInStorage=false; } _rqDict.Dirty=false;
_rqSessionState=newHttpSessionState(_rqId,_rqDict,_rqStaticObjects,_rqTimeout,_rqIsNewSession, SessionStateModule.s_config._isCookieless,SessionStateModule.s_config._mode,_rqReadonly);
_rqContext.Items.Add("AspSession",_rqSessionState);
} }
|
|
这儿涉及到的几个字段,基本上都能跟HttpSessionState提供的公共属性对应起来。需要注意的是HttpSessionState.StaticObjects是通过ASP.NET页面上的<objectRunat="Server"Scope="Session"/>类似标记静态定义的;_rqReadonly则是前面提到的<%@PageEnableSessionState="ReadOnly"%>标记设置的。
至此,状态管理器的使用与实现方法基本上分析完成,下面整理一下其使用流程:
1.构造:HttpApplication在初始化过程中调用InitModules初始化配置文件Machine.config中注册的实现了IHttpModule接口的HTTP模块;其中
SessionStateModule作为模块之一被构造并初始化;其InitModuleFromConfig方法根据配置文件中状态管理器的相关配置,构造并初始化相应的状态管理器;并根据各种条件调用CompleteAcquireState方法完成HttpSessionState的构造工作。
2.使用:HttpSessionState通过SessionDictionary实现其Item属性的状态数据管理;SessionDictionary本身由
SessionStateModule.OnReleaseState在适当的时候写回状态管理器;其他维护操作也是通过
SessionStateModule调用状态管理器的IStateClientManager接口完成的。
3.实现:状态管理器从抽象基类StateClientManager获得异步调用的封装;通过IStateClientManager接口提供给
SessionStateModule管理其初始化、释放和管理的接口。
虽然ASP.NET做了很多工作,但个人感觉还远远不够。例如InProc/OutOfProc实际上都是在内存中,只是解决了一个可靠性和数据集中同步的问题;SQLServer虽然能够解决容量、可靠性和数据集中同步的问题,但效率又受到影响。这方面.NET应该向Java好好学习一下,例如Java下
EHCache和
OSCache都提供了平滑的可配置二级(内存/硬盘)缓存介质切换,并且后者还提供了对负载均衡的简单支持,此外还有JBoss等实现的基于IP多播等实现技术的负载均衡缓存实现等等,都远远超出了ASP.NET提供的缓存机制所考虑到的范围。虽然ASP.NET也有独立的缓存机制,MS也提出了CacheApplicationBlock的参考实现,不过还是任重而道远啊,呵呵
分享到:
相关推荐
ASP.NET 中 Session 实现原理浅析 如 session的说明 使用等等
ASP.NET的 Session 详解
ASP.NET中简单的Session、Application、Cookie的小例子
本资源是用session实现的一个购物车,是用链表和session实现的购物车,希望对您有所帮助
购物实现原代码与思路(cookie 与session两种实现方式)并实现思路说明,自已做B2C网站时写的 参考意义重大~~
方法一: 在后台应用程序中设置:Session.Timeout = 1; 注意:1、此方法可以设置在一个... 您可能感兴趣的文章:php 如何设置一个严格控制过期时间的sessionjava设置session过期时间的实现方法php中实现精确设置se
在ASP.NET应用程序运行期内.不同客户的会话状态记录 可使用ASP.NET的内置对象session在服务器进行保存.本文阐 述了session不同模式的特性及具体配置方法.
使用session实现的简单购物车,没有使用数据库,便于初学者理解session功能。
在asp.net中使用session常见问题集锦
【ASP.NET编程知识】剖析Asp.Net路由系统实现原理.docx
关闭页面时清空Session (ASP.net ) (已实现)
让你了解在asp.net中触发器的实现原理
ASP.NET中几种状态管理技术的分析与比较
基于ASP.NET+C#实现的超市管理系统
详细介绍.net 项目session存入mysql数据库,有完整demo 参考地址: http://www.codeproject.com/Articles/633199/Using-MySQL-Session-State-Provider-for-ASP-NET
ASP.NET状态管理(Cookies,Session,ViewState,Application...)
主要介绍Asp.net的工作原理,以及IIS5.0和6.0的区别. ASP.NET ISAPI 扩展 (aspnet_isapi.dll) 在 IIS 进程地址空间 (inetinfo.exe) 中运行,并通过命名管道将 ASP.NET 文件类型请求转发给 ASP.NET 辅助进程。 可以...
欧柏泰克:asp.net中session的用法
基于asp.net mvc验证码的实现。
ASP.net中实现word控件原代码