陋室铭
永远也不要停下学习的脚步(大道至简至易)

作者:Patrick Y. Ng
原文地址:http://forums.asp.net/7504/ShowPost.aspx
译者:Tony Qu (来自BluePrint翻译团队)

原文最后一次更新:2004年9月21日

本文被分成两部分:
1.“理解Session State模式”——帮助你理解三种Session State的不同之处
2. FAQ


1.理解Session State模式

存储位置

InProc:session在服务器中以活动对象方式存储(aspnet_wp.exe)

StateServer: session被序列化并保存在单独的aspnet_state.exe的内存中。StateServer能够运行在另一台服务器上

SQLServer: session被序列化并保存在SQL Server中

性能:
InProc:最快,但是session数据越多,web服务器上消耗的内存也越多,它可能影响性能。

StateServer:当存储基本类型(如string,integer等)数据时,在同一个测试环境中它比InProc慢15%。如果你存储大量对象,序列化和反序列化可能影响到性能

SQLServer:当存储基本类型(如string,integer等)数据时,在同一个测试环境中它比InProc慢25%。它也有与StateServer一样的序列化性能问题。

关于Out-of-Proc(OOP,非InProc)模式的性能提示
如果你使用OOP模式(即StateServer或SQLServer),session state中的序列化和反序列化对象将成为你的主要性能消耗之一。对于基本类型,ASP.NET通过一种内部优化方法来完成序列化和反序列化。(基本类型包括所有的数字类型(如Int, Byte, Decimal,String, DateTime, TimeSpan, Guid, IntPtr和UIntPtr等))

如果你有一个session变量(如一个ArrayList对象),且它不是一个基本类型,ASP.NET将使用BinaryFormatter来进行序列化和反序列化,那可能会相对慢一些。

所以出于性能考虑,最好使用上面列出的基本类型来存储所有的session state数据。例如,如果你需要存储两个东西,名字和地址,在session state中你既可以(方法a)使用两个string session变量来存储它们,也可以(方法b)创建一个内含两个string的类来保存它们,然后把这个类对象保存在一个session变量中。出于性能考虑,你应该选择方法a。

为了进一步理解这个主题,请看FAQ中的一个问题:“序列化和反序列化如何在SqlServer和StateServer模式下工作”

健壮性
InProc:如果工作者进程(aspnet_wp.exe)进行资源回收或者应用程序域(appdomain)重启动,session state就会丢失。这是因为session state是保存在一个应用程序域的内存空间中的。对配置文件(如web.config和machine.config)的修改或者\bin目录的任何改变(例如在你使用VS编译应用程序后产生了一个新的dll)都可能引起重启动,详细请见KB324772。在1.0中,也有一个bug可能引起工作者进程重启动,但这个bug在1.1中已经修复,见KB321792。

如果你使用的是IIS6.0,你可以在IIS Manager中找到Application Pools/DefaultAppPool,其中可以看到回收(Recycling)选项卡与性能(Performace)选项卡中是否有引起IIS工作者进程(w3svc.exe)停止工作的参数。

更多有关应用程序资源回收的内容,可以看我的另一篇FAQ:
http://www.asp.net/Forums/ShowPost.aspx?tabindex=1&PostID=232621

StateServer:解决了InProc模式的session state丢失问题。允许一个webfarm在中央服务器中存储session。只能在State Server上出现失败。

SQLServer:与StateServer相似。session state的数据在SQL Server重启后仍然保留着,你也可以按照KB311209的步骤使用SQL server failover cluster

警告
InProc:它不能在web garden模式下工作,因为在这个模式下会有多个aspnet_wp.exe在同一台机器上运行。建议在使用web garden使切换到State Server或SQL Server。仅在InProc模式下支持Session_End事件。

StateServer
- 在web farm中,请确认在所有的web服务器上有相同的<machineKey>。KB313091描述了如何设置它。
- 请确保你的对象是可序列化的。详见KB312112
- 为了在web farm中的不同web服务器上维护session state,IIS Metabase中的网站应用程序路径(如\LM\W3SVC\2)应该在所有的服务器上保持一致(大小写敏感)。详见KB325056

SQLServer
- 在1.0中有一个bug,如果你在连接字符串中指定integrity security(如"trusted_connection=true"或 "integrated security=sspi"),且你打开了asp.net的身份模拟,它将不会工作。这个问题在KB324479中有描述,不幸的是这份文档中的描述和原因部分是错误的。不过已经有一个QFE fix对它作了修复,这个fix将包含在1.0 sp3中。这个问题在1.1中已经修复了。
- 请确认你的对象是可序列化的,否则你的请求可能被挂住,详见KB312112。SQLServer模式的挂起问题已经在1.1中修复,KB324479的QFE fix也修复了这个问题。1.0 sp3也对这个问题作了修复。
- 为了在web farm中的不同web服务器上维护session state,IIS Metabase中的网站应用程序路径(如\LM\W3SVC\2)应该在所有的服务器上保持一致(大小写敏感)。详见KB325056

其他资源
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/aspnetsessionstate.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/CachingArchch2.asp
http://www.411asp.net/home/tutorial/specific/web/sessions

2. FAQ问题列表
Q: session state在部分浏览器上工作,而在其他一些上不工作。为什么呢?
Q: 在InProc模式中,为什么我有时会丢失所有的session?
Q: session state在一些web服务器上工作,但是在其他服务器上不工作。
Q: 为什么session state不可用?
Q: 为什么session_end没有触发?
Q: 使用InProc模式时,为什么我的session变量频繁丢失?
Q: 在session超时或删除之后,为什么SessionID保持不变
Q: 为什么SessionID每一次请求都会改变
Q: Session.Abandon()和Session.Clear()有什么区别
Q: session的Timeout属性是一个滑动超时值吗?
Q: 我可以在ASP.NET和ASP之间共享session吗?
Q: 我可以在web应用程序(例如虚拟目录或者IIS的应用程序)间共享session state吗?
Q: 在session state中可以存储哪些类型的对象?
Q: 为什么我的请求在切换到SQLServer模式之后挂住了?
Q: 为什么Response.Redirect和Server.Transfer在Session_End中不工作?
Q: 在Session_End中,我可以获得一个有效的HttpSessionState对象和HttpContext对象吗?
Q: 在web service中如何使用session?
Q:我正在写一个HttpHandler,为什么session stae不工作?
Q: 我正在使用web farm,并且每当我重定向到其他服务器时,session state就会丢失?
Q: 如果使用cookieless,我该如何从一个HTTP页面重定向到一个HTTPS页面?
Q: session state有没有一个锁机制来安排对session的访问顺序?
Q: 我该如何检测一个session过期,然后重定向到另一个页面
Q: 在Session_End中,我尝试使用SQL做一些清理工作,但是失败了,请问为什么?
Q: 我使用的是SQLServer模式,为什么我的session不会过期
Q: 我有一个以htm为扩展名的frameset页面,并且我发觉其中包含的每个帧在第一次请求时都有一个不同的SessionID,这是为什么?
Q: 我将EnableSessionState设置为ReadOnly,但是在InProc模式下,我仍然可以修改session,为什么?
Q: 我将cookieless设置为true,在Redirect之后session变量丢失了,为什么?
Q: 将cookieless设置为true有哪些缺点
Q: 在InProc模式下,我用编程方式改变了session的超时时间,它触发了Session_End,为什么?
Q: 在SQLServer模式下,我可以把session state保存在除tempdb之外的数据库中吗?
Q: 如何防止将未加密的字符串放在我的连接字符串汇总?
Q: 在使用SQLServer模式时,我需要怎样的SQL权限?
Q: 我可以自己写定制的session state模式吗?
Q: 在SQLServer或StateServer模式下,序列化和反序列化如何工作?
Q: 我该如何让我的state server更安全?
Q: 我能否可以使用非global.asax中的处理程序来订阅SessionStateModule.End事件?
Q: 不同的应用程序可以把他们的session state保存在同一个SQL Server上的不同数据库中吗?


Q: session state在部分浏览器上工作,而在其他一些上不工作。为什么呢?
A: 估计你没有使用cookieless,你必须保证你的浏览器支持cookie。请参考这份KB:http://support.microsoft.com/default.aspx?scid=kb;EN-US;q316112

Q: 在InProc模式中,为什么我有时会丢失所有的session?
A: 请见理解session state模式的健壮性部分

Q: session state在一些web服务器上工作,但是在其他服务器上不工作。
A: 可能是机器名的问题,见http://support.microsoft.com/default.aspx?scid=kb;EN-US;q316112

Q: 为什么session state不可用?
A:
- 首先,检查web.config、machine.config和Page标签来确认你启用了session state
参考资料:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconsessionstate.asp
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconpage.asp
- 请注意session state并非在任何地方、任何时间都可以使用,它仅在HttpApplication.AcquireRequestState事件之后可用。例如,在 global.asax中的Application_OnAuthenticateRequest处理程序中,session state不可用
- 请确认System.Web.SessionState.SessionStateModule已包含在配置文件的< httpModules>节中。一个常见的例子是,出于性能考虑,SharePoint应用程序会把这个模块从web.config文件中移除,因此导致session不可用

Q: 为什么session_end没有触发?
A: 这是最常见的问题之一
1. 请记住session_end仅在InProc模式中可用
2. 关闭浏览器,session_end是不会触发的。HTTP是一种无状态协议,服务器没有办法知道你的浏览器是否已经关闭。
3. 当有n分钟(n=timeout值)的无操作或调用Session.Abandon时,Session_End才会触发
4. 对于情况1而言,Session_End将由一个后台线程触发,这表示:
a. Session_End中的代码使用工作者进程账号运行,如果你访问如数据库这样的资源时,可能会有权限问题。
b. 如果在Session_End中发生错误,程序不会通知发生了什么
5. 对于情况2而言,为了让Session_End触发,session state必须先存在。这意味着你必须在session state中存储一些数据,并且已经完成了至少一个请求
6. 还是对于情况2而言,Session_End仅在被丢弃的session被找到的时候才会触发。这样的话,如果你在同一个请求中创建并丢弃一个 session,由于session没有被保存,因此也不会被找到,Session_End将不会被调用。这是v1.0和v1.1中的bug。

Q: 使用InProc模式时,为什么我的session变量频繁丢失?
A: 有可能是应用程序资源回收引起的,见http://support.microsoft.com/default.aspx?scid=kb;en-us;Q316148
在v1.0中有一个bug可能会导致工作者进程重启动。在v1.1和v 1.0sp2中已经修复。见http://support.microsoft.com/default.aspx?scid=kb;EN-US;321792

关于应用程序资源回收的详细信息,请见我的另一篇:FAQhttp://www.asp.net/Forums/ShowPost.aspx?tabindex=1&PostID=232621

Q: 在session超时或删除之后,为什么SessionID保持不变
A: 尽管在超时周期之后session state过期,sessionID将一直保持到浏览器session过期为止,也就是说,一个相同的sessionID可以有多次session超时,但是始终对应着一个相同的浏览器实例。

Q: 为什么SessionID每一次请求都会改变
A: 如果你的应用程序从未在session state中存储过数据。在这种情况下,那么每次请求都会创建一个新的session state(ID也是新的),但是不会被存储,因为里面什么数据都没有。

尽管如此,有两种例外可能产生相同的Session ID
- 如果用户使用相同的浏览器实例来请求另一个使用session state的页面,那么你每次获得的Session ID是相同的。详见“在session超时或删除之后,为什么SessionID保持不变?”
- 如果使用了Session_OnStart事件,即使session为空,asp.net也会保存session state。

Q: Session.Abandon()和Session.Clear()有什么区别
A: 主要的区别在于,如果你调用Session.Abandon(), Session_End将被触发(仅在InProcxi下适用),在下一个请求中,Session_Start触发。而Session.Clear()仅仅是清除数据,但没有删除session。

Q: session的Timeout属性是一个滑动超时值吗?
A: Session的Timeout是一个滑动过期时间,意思是一旦你的页面访问session state,过期时间就会向挪。注意,只要页面没有被禁用,在请求时页面就会自动访问session

Q: 我可以在ASP.NET和ASP之间共享session吗?
A:不可以。但是有一篇文章讲到了如何来绕过这个问题:http://www.msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/ConvertToASPNET.asp

当然也有一些第三方解决方案。

Q: 我可以在web应用程序(例如虚拟目录或者IIS的应用程序)间共享session state吗?
A:不能。

Q: 在session state中可以存储哪些类型的对象?
A:这是由你使用的模式决定的
- 如果你使用的是InProc模式,存储在session state中的对象是活对象,那么你就可以存储你创建的任何对象
- 如果你使用的是SQLServer或State Server模式,当处理一个请求时,session state中的对象对象将被序列化和反序列化,所以请确认你的对象都是可序列化的,而它们的类都作了可序列化标记。如果没有,session state将不会成功存储。在v1.0中,有一个bug,当这个问题发生时,如果使用SQLServer模式,请求可能在不知情的情况下被挂起。挂起的问题在v1.1和v1.0 sp3中已经修复。KB324479的QFE fix也包含了对这一问题的修复。

更多信息请见:http://support.microsoft.com/directory/article.asp?ID=KB;EN-US;q312112

Q: 为什么我的请求在切换到SQLServer模式之后挂住了?
A:请看问题“在session state中可以存储哪些类型的对象?”的答案

Q: 为什么Response.Redirect和Server.Transfer在Session_End中不工作?
A:Session_End是在服务器内部触发的,它基于一个内部的计时器。因此,在事件触发时,与任何HttpRequest对象无关。这也是为什么Response.Redirect 和Server.Transfer不工作的原因。

Q: 在Session_End中,我可以获得一个有效的HttpSessionState对象和HttpContext对象吗?
A:  你可以获得httpSessionState对象,你可以使用'Session'来访问该对象。但是你无法访问HttpContext,因为这个事件和请求没有任何关系。

Q: 在web service中如何使用session?
A: 需要在调用方使用一些技巧,你必须保存web服务使用的cookie。请见关于HttpWebClientProtocol.CookieContainer的MSDN文档。

尽管如此,如果你是通过代理对象从你的页面调用web服务,由于架构限制,web服务和你的页面无法共享session state。

如果你通过redirect调用web服务,这是可以完成的

Q:我正在写一个HttpHandler,为什么session stae不工作?

A: 你的HttpHandler接口必须实现标记接口IRequiresSessionState或IReadOnlySessionState,方能使用session state。

Q: 我正在使用web farm,并且每当我重定向到其他服务器时,session state就会丢失?
A: 为了在web farm中的不同服务器之间维护session state,IIS Metabase中的网站应用程序路径(例如 \LM\W3SVC\2)应该在所有的web服务器上保持一致(大小写敏感)。详见KB325056

Q: 如果使用cookieless,我该如何从一个HTTP页面重定向到一个HTTPS页面?
A: 尝试使用下面的代码:
String originalUrl = "/fxtest3/sub/foo2.aspx";
String modifiedUrl = "https://localhost" + Response.ApplyAppPathModifier(originalUrl);
Response.Redirect(modifiedUrl);

Q: session state有没有一个锁机制来安排对session的访问顺序?
A:session state实现了读写锁定机制:
- 对session state有写权限(如<%@ Page EnableSessionState="True" %> )的页面或帧将获得这个session的写锁,直到请求结束。
- 对session state有读权限(如<%@ Page EnableSessionState="ReadOnly" %> )的页面或帧将获得这个session的读锁,直到请求结束。
- 读锁会阻塞写锁;读锁不会阻塞读锁;写锁会阻塞所有的读锁和写锁
- 这也是为什么当两个帧同时拥有session的访问权限时,一个帧必须等待另一帧先完成

Q: 我该如何检测一个session过期,然后重定向到另一个页面
A: 这是经常要遇到的问题,但不幸的是没有很简单的方法来完成它。我们将期待在一个主要版本中实现它。同时,如果你使用cookie,你可以在cookie中存储一个标志,这样你就可以区分新浏览器+新session及旧浏览器+过期session,下面的代码在session过期时会重定向到一个过期页面。
void Session_OnStart(Object sender, EventArgs e) {
    HttpContext context = HttpContext.Current;
    HttpCookieCollection cookies = context.Request.Cookies;

    if (cookies["starttime"] == null) {
        HttpCookie cookie = new HttpCookie("starttime", DateTime.Now.ToString());
        cookie.Path = "/";
        context.Response.Cookies.Add(cookie);
    }
    else {
        context.Response.Redirect("expired.aspx");
    }
}

Q: 在Session_End中,我尝试使用SQL做一些清理工作,但是失败了,请问为什么?
A: 首先,session_End仅在InProc模式下支持。
第二,Session_End是用运行工作者进程(aspnet_wp.exe)的帐号运行的,这个账号可以在machine.config中指定。因此,在你的Session_End中,如果使用integrity security连接SQL,它将使用工作者进程账号身份连接,这可能会引起登录失败,这要看你的SQL安全设置了。

Q: 我使用的是SQLServer模式,为什么我的session不会过期
A: 在SQLServer模式下,session过期是由SQL Agent使用一个注册任务完成的,请确认你的SQL Agent是否已经运行。

Q: 我有一个以htm为扩展名的frameset页面,并且我发觉其中包含的每个帧在第一次请求时都有一个不同的SessionID,这是为什么?
A: 原因是你的frameset页面是一个htm文件而不是一个aspx页面

在通常情况下,如果一个frameset页为一个aspx文件,当你请求该页面时,会首先发请求给web服务器,你会收到一个asp.net session cookie(其中保存着session id),然后浏览器会为frame发送一个单独的请求,而每个请求将拥有相同的session id。

然而,因为你的页面是一个htm文件,第一个请求就不会获得任何session cookie,因为页面是由asp处理的而非asp.net,然后浏览器会为每个帧发送单独的请求。但是,这次每个单独的请求将不会持有任何 session id,这样的话每个帧将创建自己的session。这也是为什么你在每个帧中看到的session id都不同。最后一个请求将赢得胜利,因为它将覆盖前两个请求写入的cookie。如果你刷新一次,你将看到它们拥有了相同的session id。

这个行为是设计所决定的,简单的解决方法就是将frameset页面改称aspx
 
Q: 我将EnableSessionState设置为ReadOnly,但是在InProc模式下,我仍然可以修改session,为什么?
A: 尽管那些EnableSessionState被设置为ReadOnly,但是在InProc模式中,用户仍然可以修改session。唯一的区别在于session在请求中不会被锁住,这一限制是设计所决定的。对于这一点没有在msdn中提到我表示抱歉。

Q: 我将cookieless设置为true,在Redirect之后session变量丢失了,为什么?
A: 如果你使用的是cookieless,你必须使用相对路径(如..\hello.aspx),而不是绝对路径(如\foo\bar\hello.aspx)。如果你使用的是绝对路径,ASP.NET不会将session id保存在url中。

Q: 将cookieless设置为true有哪些缺点
A: 设置cookieless=true表示一些潜在的规则,主要有:
1. 你不能在你的页面中使用绝对路径
2. 在http和https之间切换的话,你必须做一些额外的动作
3. 如果你的客户发送了一个链接到一个朋友,URL将包含session id,两个用户可以在同一时间使用相同的session id

Q: 在InProc模式下,我用编程方式改变了session的超时时间,它触发了Session_End,为什么?
A: 这是InProc的一个bug。如果你更改session的timeout值为另一个值,Session_End将被调用(但不会调用Session_Start)。我们期待在v2.0中能够修复这个错误。

Q: 在SQLServer模式下,我可以把session state保存在除tempdb之外的数据库中吗?
A: 是的。见KB311209。

Q: 如何防止将未加密的字符串放在我的连接字符串汇总?
A: 见sql trusted connection或者将连接字符串以加密数据形式保存在注册表中。详情请见,KB329250和KB329290。

Q: 在使用SQLServer模式时,我需要怎样的SQL权限?
A: 调用者需要对下面的存储过程拥有EXEC权限,
dbo.TempGetAppID
dbo.TempGetStateItem
dbo.TempGetStateItemExclusive
dbo.TempReleaseStateItemExclusive
dbo.TempInsertStateItemLong
dbo.TempInsertStateItemShort
dbo.TempUpdateStateItemLong
dbo.TempUpdateStateItemShort
dbo.TempUpdateStateItemShortNullLong
dbo.TempUpdateStateItemLongNullShort
dbo.TempRemoveStateItem
dbo.TempResetTimeout
在v1.1中,你也需要对下面的存储过程拥有EXEC权限
dbo.TempGetStateItem2
dbo.TempGetStateItemExclusive2

请注意存储过程的拥有者必须对session state表(dbo.ASPStateTempSessions和 dbo.ASPStateTempApplications)拥有SELECT/INSERT/UPDATE/DELETE 权限。通常,拥有者是执行installsqlstate.sql(或者持久版本,见KB311209)的帐号来安装sql session state需要的表、存储过程、数据库

也请注意,如果你的session state表在tempdb中(默认情况下)如果你对SQL Server进行资源回收,所有在这张表上的权限设置将丢失。

Q: 我可以自己写定制的session state模式吗?
A:(待翻译)

Q: 在SQLServer或StateServer模式下,序列化和反序列化如何工作?
A: (待翻译)

Q: 我该如何让我的state server更安全?
A:如果state server和web server运行在一台机器上,通过设置HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ aspnet_state\Param ters\AllowRemoteConnection的dword项为0,可以让state server仅在本地运行。这样就可以防止远程客户端连见到state server上。这一特性在v1.1中可用,在v1.0 sp3中也有。

state server必须受防火墙保护,以防止外部连接以保证真正安全。默认的端口是TCP 42424,你可以设置HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ aspnet_state\Param ters\Port来改变它。如果是本地模式,除了127.0.0.1以外,屏蔽所有外来连接;如果是远程模式,显式的禁用所有的地址,除了对wev服务器的连接。

使用IPSec是另一种保护state server的方式。

Q: 我能否可以使用非global.asax中的处理程序来订阅SessionStateModule.End事件?
A: 答案是否定的。当SessionStateModule触发End事件时,只有定义在global.asax中的方法才会被触发

这是出于安全原因考虑的才对此进行限制。假设asp.net允许用户使用的其他的处理程序来处理End事件。在这种情况下,用户通常使用一个页面方法作为处理程序,当你在事件订阅时传入处理程序,处理程序将与你的程序运行在的HttpApplication实例关联。请注意, HttpApplication实例会被回收来处理其他请求。这样的话,当End事件触发时,asp.net将调用处理程序,而与之关联的 HttpApplication实例已经被另一个请求所使用,这样的情况将引发各种各样的问题。为了避免这种危险,在v1.0中决定进调用 Global.asax中定义的方法。希望你们都可以忍受这一限制。

Q: 不同的应用程序可以把他们的session state保存在同一个SQL Server上的不同数据库中吗?
A: 答案是肯定的。详情请见:http://support.microsoft.com/default.aspx?scid=kb;EN-US;836680

posted on 2011-11-19 18:36  宏宇  阅读(262)  评论(0编辑  收藏  举报