IIS提示 Session state has created a session id, but cannot save it because the response was already…

转载自 http://hi.baidu.com/yandavid

FIX: Session state has created a session id, but cannot save it because the response was already flushed by the application.

在事件查看器中,你能看到这样的描述。Because Session.SessionId is not called before the page is flushed, when the view decides that it needs to save some session information after the page has flushed, it’s too late for the application to write the SessionId to the users cookie collection and so the above error is thrown.

解決方式如下在Global.cs加入代码:So easy. It works!

 

voidSession_Start(objectsender,EventArgse) {    // Code that runs when a new session is started    stringsessionId=Session.SessionID; } Solutions/tricks :

Avoid flushing in Page_Load as already said

Desactivate session state on the page (EnableSessionState)

Use the SessionID trick before the flush

Use Response.End() in place of .Flush() if you don’t care about errors which can occur after your flush

 

Event code: 3005 Event message: An unhandled exception has occurred. Event time: 2011-8-19 10:21:03 Event time (UTC): 2011-8-19 2:21:03 Event ID: 39e2d6d5bf0c4b7db31aa0e4d2fc8127 Event sequence: 6 Event occurrence: 1 Event detail code: 0
Application information:     Application domain: /LM/W3SVC/1147867452/Root-1-129581940607186250     Trust level: Full     Application Virtual Path: /     Application Path: C:\Web\     Machine name: MRTEST-WTUISY3 Process information:     Process ID: 2516
Process name: w3wp.exe     Account name: NT AUTHORITY\NETWORK SERVICE Exception information:     Exception type: HttpException     Exception message: Session state has created a session id, but cannot save it because the response was already flushed by the application.    at System.Web.SessionState.SessionIDManager.SaveSessionID(HttpContext context, String id, Boolean& redirected, Boolean& cookieAdded)    at System.Web.SessionState.SessionStateModule.CreateSessionId()    at System.Web.SessionState.SessionStateModule.DelayedGetSessionId()    at System.Web.SessionState.SessionStateModule.ReleaseStateGetSessionID()    at System.Web.SessionState.SessionStateModule.OnReleaseState(Object source, EventArgs eventArgs)    at System.Web.SessionState.SessionStateModule.OnEndRequest(Object source, EventArgs eventArgs)    at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()    at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Request information:     Request URL: http://127.0.0.1:8090/?tag/im     Request path: /index.aspx     User host address: 192.168.68.45     User:      Is authenticated: False     Authentication Type:      Thread account name: NT AUTHORITY\NETWORK SERVICE
Thread information:     Thread ID: 1     Thread account name: NT AUTHORITY\NETWORK SERVICE     Is impersonating: False     Stack trace:    at System.Web.SessionState.SessionIDManager.SaveSessionID(HttpContext context, String id, Boolean& redirected, Boolean& cookieAdded)    at System.Web.SessionState.SessionStateModule.CreateSessionId()    at System.Web.SessionState.SessionStateModule.DelayedGetSessionId()    at System.Web.SessionState.SessionStateModule.ReleaseStateGetSessionID()    at System.Web.SessionState.SessionStateModule.OnReleaseState(Object source, EventArgs eventArgs)    at System.Web.SessionState.SessionStateModule.OnEndRequest(Object source, EventArgs eventArgs)    at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()    at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

ASP.NET Session详解

当用户在 Web 应用程序中导航ASP.NET 页时,ASP.NET 会话状态使您能够存储和检索用户的值。HTTP 是一种无状态协议。这意味着Web 服务器会将针对页面的每个HTTP 请求作为独立的请求进行处理。服务器不会保留以前的请求过程中所使用的变量值的任何信息。

ASP.NET 会话状态将来自限定时间范围内的同一浏览器的请求标识为一个会话,当每个用户首次与这台服务器建立连接时,他就与这个服务器建立了一个Session,同时服务器会自动为其分配一个SessionID,用以标识这个用户的唯一身份。Session提供用于在该会话持续期间内保留变量值的方法。默认情况下,将为所有 ASP.NET 应用程序启用ASP.NET 会话状态.为ASP.NET 应用程序启用会话状态时,将检查应用程序中每个页面请求是否有浏览器发送的 SessionID 值。如果未提供任何 SessionID 值,则 ASP.NET 将启动一个新会话,并将该会话的 SessionID 值随响应一起发送到浏览器。System.Web.SessionState.HttpSessionState.SessionID 值都以明文的形式发送

会话变量可以是任何有效的.NET Framework 类型, 注意:当使用InProc 以外的会话状态模式时,会话变量类型必须为基元 .NET 类型或可序列化的类型。这是因为会话变量值存储在外部数据存储区中。

默认情况下,SessionID 值存储在 Cookie 中。但也可以将应用程序配置为在“无 Cookie”会话的 URL 中存储 SessionID 值。只要一直使用相同的 SessionID 值来发送请求,会话就被视为活动的。如果特定会话的请求间隔超过指定的超时值(以分钟为单位),则该会话被视为已过期。使用过期的 SessionID 值发送的请求将生成一个新的会话。

.NET提供了5种保存Seission的方式:

方式名称 存储方式 性能
Off 设置为不使用Session功能
InProc 设置为将Session存储在进程内,就是ASP中的存储方式,这是默认值。 性能最高
StateServer 设置为将Session存储在独立的状态服务中。通常是aspnet_state.exe进程. 性能损失10-15%
SQLServer 设置将Session存储在SQL Server中。 性能损失10-20%
Customer 自定制的存储方案 由实现方式确定

 

使用SqlServer模式搭建Session服务器

(1)服务器端配置

使用SqlServer模式搭建Session服务器端有两种方式. ASP.NET 1.0和1.1版本请使用方式a, 2.0即以上版本请使用方式b.

a.使用SQL文件创建Session数据库

在ASP.NET 1.0和1.1 版本中, 只能使用这种方式.对于2.0及其以上版本,请使用aspnet_regsql.exe工具.(当然此方法也通用2.0版本)

.net提供了数据库安装脚本,可以在机器的windows文件夹中找到:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlState.sql

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ InstallSqlStateTemplate.sql

根据ASP.NET的版本不同, 需要使用不同的SQL脚本. ASP.NET主要有1.1和2.0两个版本,可以在不同的版本文件夹找到这两个SQL.

InstallSqlState.sql 是创建默认名称的数据库”[ASPState]”.此SQL可以直接运行.

InstallSqlStateTemplate.sql 可以使用自己指定的数据库保存数据.此SQL需要自己修改后运行, 打开SQL文件将其中 [DatabaseNamePlaceHolder] 替换为自己指定的数据库名称.

执行installsqlstate.sql时不需要指定数据库,可以在任意数据库上执行.此SQL会自己创建新的数据库

b. 使用aspnet_regsql.exe工具

ASP.NET 2.0版本后微软提供了aspnet_regsql.exe工具可以方便的配置Session数据库.该工具位于 Web 服务器上的”系统根目录\Microsoft.NET\Framework\版本号”文件夹中.

使用举例:

aspnet_regsql.exe -S . -U sa -P 123456 -ssadd -sstype p

-S参数:表示数据库实例名称. 可以用”.”表示本机.

-U和-P参数:表示用户名和密码.

-E参数: 可以再-U –P 与 -E中选择一组. –E表示以当前系统用户通过windows身份验证登录数据库, -U -P则是使用SqlServer用户登录数据库.

-ssadd / –ssremove 参数: -ssadd表示是添加Session数据库, -ssremove表示移除Session数据库.

sstype 参数:

 

选项 说明
t 将会话数据存储到 SQL Server tempdb 数据库中。这是默认设置。如果将会话数据存储到 tempdb 数据库中,则在重新启动 SQL Server 时将丢失会话数据。
p 将会话数据存储到 ASPState 数据库中,而不是存储到 tempdb 数据库中。
c 将会话数据存储到自定义数据库中。如果指定 c 选项,则还必须使用 -d 选项包括自定义数据库的名称。

 

 

(2)Session客户端设置

此房是同样需要Web应用程序修改Web.Config中的节点.如果使用默认的数据库(ASPState库), 则配置如下:

 

<sessionState mode="SQLServer" sqlConnectionString="server=192.168.9.151; uid=sa; pwd=123456;"/>

 

 

如果使用了自定义的数据库名称,则还需要制定allowCustomSqlDatabase属性并在数据库连接串中指定数据库:

 

<sessionState mode="SQLServer" allowCustomSqlDatabase="true" sqlConnectionString="server=192.168.9.151; DataBase=MyAspState;uid=sa; pwd=123456;" />

 

 

使用经验与技巧总结

下面是SessionID, Session_End时间, StatServer模式 和 SqlServer模式的各种经验和技巧总结.

(1)StateServer模式:

1.在web farm中,请确认在所有的web服务器上有相同的

2. 要保存在Session中的对象是可序列化的。

3.为了在web farm中的不同web服务器上维护session state,IIS Metabase中的网站应用程序路径(如\LM\W3SVC\2)应该在所有的服务器上保持一致(大小写敏感).

4. ASP.NET处理Session是在Machine.Config中配置的HttpModuel模块, 在.NET的安装目录下的Config文件夹中, 查看Web.Config(1.1版本是在Machine.Config):

确认此模块是否存在.

5.StateServer不支持负载均衡, 所以如果大并发推荐使用SqlServer模式, 可以享受到SqlServer的高性能和安全性.虽然存储效率会有下降.

6.需要让所有机器的MachineKey相同.在Machine.Config中配置:

 

<machineKey validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" decryptionKey="123456789012345678901234567890123456789012345678" validation="SHA1" decryption="Auto"/>

 

 

(2)SqlServer模式:

1. 要保存在Session中的对象是可序列化的。

2. 如果使用了默认是数据库, 则在客户端配置文件中的数据库链接字符串的用户,需要拥有ASPState和tempdb两个库的dbowner权限.

3. 在SQLServer模式下,session过期是由SQL Agent使用一个注册任务完成的,要确认SQL Agent已经运行。否则无法清理过期的Session数据, 会导致数据库数据一直增加.

4. 如果使用SqlServer模式时, 对于Web场中的各服务器的 ASP.NET 应用程序路径必须是相同的。请在 IIS 配置数据库中对 Web 场中的所有 Web 服务器进行 Web 站点的应用程序路径同步。大小写一定要相同,因为 Web 站点的应用程序路径是区分大小写的。

5.需要让所有机器的MachineKey相同.在Machine.Config中配置:

 

<machineKey validationKey="1234567890123456789012345678901234567890AAAAAAAAAA" decryptionKey="123456789012345678901234567890123456789012345678" validation="SHA1" decryption="Auto" />

 

 

(3)Session:

1. 不能直接通过Session服务器在ASP.NET和ASP之间共享Session. 请使用微软提供的解决方案:

http://msdn.microsoft.com/zh-cn/library/aa479313.aspx

2. 在不同的应用程序或一个网站的不同虚拟目录之间无法共享Session

3. Session的过期时间是滑动时间.

4. Session存储.NET自带的值类型性能最优. 存储对象会降低性能.

(4)SessionID:

1.SessionID 还可以保存在URL上, 设置Web.Config文件中的System.Web/sessionState节点的Cookiesless属性即可:

2. 一般在Session超时或删除之后,SessionID保持不变. 因为Session过期后会在服务器端清除数据, 但是SessionID保存在用户浏览器上, 所以只要浏览器不关闭则HTTP头中的SessionID保持不变.

3.关闭浏览器后再访问, SessionID会不同.

4.每打开一个IE6窗口, SessionID都不同, 在IE6中两个窗口的Session不能共享.

5.FireFox的标签页和新的FireFox窗口, SessionID都相同, 在FF的窗口和标签页上Session能共享.

6.对于包含FrameSet的页面,比如:

如果后缀名是.htm并且.htm文件没有交给ASP.NET的ISAPI处理, 那么根据服务器速度在每个Frame页面生成不同的SessionID, 再刷新后相同都等于最后一个SessionID.

解决办法是将.htm后缀改成.aspx, 或者将.htm文件交给ASP.NET的ISAPI处理.

(5)Session_End事件:

1. Session_End仅在InProc模式中可用

2. 关闭浏览器,Session_End是不会触发的。HTTP是一种无状态协议,服务器没有办法知道你的浏览器是否已经关闭。

3. 当Session因为时间过期或调用Session.Abandon时,Session_End才会触发.Session.Clear()仅仅是清除数据,但没有删除session。

4. Session_End由一个后台线程触发,使用工作者进程账号运行. 所以程序不会通知发生的错误.

5. 在Session_End访问数据库要考虑权限问题. Session_End是用运行工作者进程(aspnet_wp.exe)的帐号运行的,这个账号可以在machine.config中指定。因此,在Session_End中,如果使用integrity security连接SQL,它将使用工作者进程账号身份连接,这可能会引起登录失败.

6.因为Session_End是有独立线程出发的, 所以在Session_End中无法使用HttpContext对象(Request,Response,Server等对象都在HttpContext中), 即无法使用 Response.Redirect 和Server.Transfer等方法.

 

你的站点有被客户投诉很慢吗?是不是查了很多遍还是没有完全解决?是不是数据库没有发现异常,CPU也没有异常,内存占用量没有异常,GC计数没有异常,硬盘IO也没有异常,带宽没有异常,线路没有异常,没有丢包,但就是被投诉?而且还是阵发性的,有某个用户投诉慢的时候,你访问却很快,本地又复现不出来?今天你访问了博客园真是太幸运了,也许这里就有你想要的一个答案——假如你的站点动态处理了图片或者其他原本不会动态处理的非网页资源的话。没想到吧,这是Session惹的祸!

如果只希望禁用应用程序的某个特定页的会话状态,则可以将 @ Page 指令中的 EnableSessionState 值设置为 false。还可将 EnableSessionState 值设置为 ReadOnly 以提供对会话变量的只读访问,则对只读会话信息的请求不会导致对会话数据的独占锁定。。对于自定义的一个实现了IHttpHandler接口的类,只要同时实现IReadOnlySessionState,即可达到EnableSessionState=”ReadOnly”的效果。对会话数据的只读请求可能仍需等到解除由会话数据的读写请求设置的锁定。

并发请求和会话状态

        对 ASP.NET 会话状态的访问专属于每个会话,这意味着如果两个不同的用户同时发送请求,则会同时授予对每个单独会话的访问。但是,如果这两个并发请求是针对同一会话的(通过使用相同的 SessionID 值),则第一个请求将获得对会话信息的独占访问权。第二个请求将只在第一个请求完成之后执行。(如果由于第一个请求超过了锁定超时时间而导致对会话信息的独占锁定被释放,则第二个会话也可获得访问权。)

不过验证码的问题还不是特别好解决,因为为了避免信息泄露的问题,验证码的答案通常是存在在Session里面的,而通常的设计也是一访问验证码图片,就会将验证码答案写到Session里面。所以,很不幸,文中提到的那个解决办法是行不通的,至少不是能够直接行得通的。这个问题有没有救药?有,当然有了。怎么救?哈,那就有劳你自己开动脑筋了,我这里算是点到即止了。