完全替换 ASP.Net 的会话

Posted

技术标签:

【中文标题】完全替换 ASP.Net 的会话【英文标题】:Replacing ASP.Net's session entirely 【发布时间】:2012-02-17 20:46:53 【问题描述】:

ASP.Net 会话看起来非常适合传统的 WebForms 应用程序,但它们所做的一些事情对于现代 AJAX 和 MVC 应用程序来说是一个严重的问题。

具体来说,访问 ASP.Net 提供程序只有 3 种方式:

    锁定读写(默认)- 会话从AcquireRequestState 触发锁定,直到ReleaseRequestState 触发。如果浏览器一次发出 3 个请求,它们将在服务器上排队。这是 MVC 2 中的唯一选项,但 MVC 3 允许...

    非锁定只读 - 会话未锁定,但无法保存。这似乎是unreliable though,因为某些读取似乎再次锁定了会话。

    会话已禁用 - 任何读取或写入会话的尝试都会引发异常。

但是,对于现代 MVC 应用程序,我会同时发生很多 AJAX 事件 - 我不希望它们在服务器上的队列中,但我确实希望它们能够写入会话。

我想要的是第四种模式:脏读,最后写获胜

我认为(很高兴得到纠正)唯一的方法是。我可以写我的own provider,但 ASP 仍会使用它支持的 3 种模式之一来调用它。有没有办法让 ASP.Net 支持乐观并发?

这让我用一个基本上做同样事情但不锁定的新类替换了对会话的所有调用——这很痛苦。

我希望尽可能多地保留当前会话的内容(最重要的是各种日志中的会话 ID),同时尽量减少代码替换。有没有办法做到这一点?理想情况下,我希望 HttpContext.Current.Session 指向我的新类,但没有 ASP.Net 锁定任何请求。

有没有人做过类似的事情?对于所有的 AJAXey MVC 应用程序来说,这似乎很奇怪,这是 ASP 的一个新问题。

【问题讨论】:

如果您编写自己的提供程序。即使 ASP.NET 要求您锁定会话,谁还在强迫您锁定?只需忽略锁定提示并随意进行脏读即可。 @HasanKhan - 我认为(我想肯定知道)SetAndReleaseItemExclusive(保存值的方法)只有在 ReleaseRequestState 事件期间被调用,如果已应用锁定。如果有某种方法可以欺骗,我想从做过的人那里知道;我不能成为第一个需要乐观并发会话的人,我宁愿不重新发明***。 “我不希望他们在服务器上的队列,但我确实希望他们能够写入会话。”不,不,你没有。如果他们需要写入会话,他们需要轮流避免不一致的写入。使用选项 3 获取尽可能多的请求,以尽可能减少阻塞,并保持不变。 @Ben - 我不关心不一致的写入。正如我在问题中所说的那样,我想要最后写入获胜。对于我的应用程序(我怀疑还有更多)会话包含用户选项(页面大小、当前排序字段、购物篮等)或用于优化的缓存值 - 在任何一种情况下,对于我的应用程序来说,我都会使用 last-write-wins 模型想。我敢肯定有很多应用程序的当前锁定模型是完美的,但我不能是唯一一个在会话中寻找乐观并发的开发人员。 @Aristos 就像我一开始说的,锁独占适用于很多情况。我并不是说乐观并发应该是默认的,我是在问其他人是否已经这样做或找到了更好的方法来实现它。 【参考方案1】:

首先我说MS asp.net已经锁定了“asp.net处理页面”会话的核心,在asp.net的“webengine4.dll”dll中的某处4

我说从 MS asp.net act correct and lock the session this way 的角度来看,因为 asp.net 无法知道我们在会话中保留什么类型的信息,因此可以做出正确的“最后写入获胜”。

同样正确的是锁定会话整页,因为这样可以为您的程序提供非常重要的同步,从现在的经验来看,我说您需要它对于您的大部分操作。在我用我的替换 ms 会话后,我需要在所有操作上进行全局锁定,否则我会遇到双重插入、双重操作等问题。

我给出了一个我在这里认识到的问题的示例。会话数据保存在作为键列表的 SessionSateItemCollection 上。当会话读取或写入此集合时,将它们全部一起完成。那么让我们来看看这个案例吧。

我们在会话中有树变量,“VAR1”、“VAR2”、“VAR3”

第 1 页。 getsession(实际上是获取所有数据并将它们放在列表中) 会话[VAR1] = "data1"; 保存会话() 结果是 VAR1=data1, VAR2=(var2 的最后一个数据), VAR3=(var3 的最后一个数据)

第 2 页。 getsession(实际上是获取所有数据并将它们放在列表中) 会话[VAR2] = "data2"; 保存会话() 结果是 VAR1=(var1 的最后一个数据), VAR2=data2, VAR3=(var3 的最后一个数据)

第 3 页。 getsession(实际上是获取所有数据并将它们放在列表中) 会话[VAR3] = "data3"; 保存会话() 结果是 VAR1=(var1 的最后一个数据), VAR2=(var2 的最后一个数据) VAR3="data3"

请注意,每个页面都有数据的克隆,因为他从会话介质中读取它们(例如,当它们最后从数据库中读取时)

如果我们让这 3 个页面在没有锁定的情况下运行,我们实际上并没有脏读,也没有“最后一次写入获胜” - 我们这里有的是“最后一次写入破坏其他页面”,因为如果你再想一想,当 VAR1 改变时,为什么 VAR2 和 VAR3 保持不变?如果您在其他地方更改了 VAR2 怎么办。

出于这个原因,我们不能在没有锁的情况下让它保持原样。

现在用 20 个变量对其进行成像……完全一团糟。

可能的解决方案

由于许多池的asp.net的多线程,跨池和跨计算机保持相同数据的唯一方法是拥有一个公共数据库,或者像asp.net State Service这样的所有进程程序通用的.

我选择拥有一个公共数据库,这比为该提议创建一个程序更容易。

现在,如果我们将我们制作的cookie连接到用户到会话的数据用户,我们使用完全自定义的数据库字段来控制数据我们知道我们喜欢锁定什么,不喜欢什么,如何保存或更改,或比较和保留最后一次写入的胜利数据,我们可以完成这项工作,并完全禁用 asp.net 会话,它是为所有人制作的通用会话保持器需要,但不能处理像这样的特殊情况。

asp.net 能否在未来的版本中使这项工作,是的,他可以通过创建一个名为脏的额外字段,并在会话保存数据,使用脏字段或类似的东西进行数据合并,但可以现在不要让它按原样工作 - 至少从我到目前为止发现的情况来看。 实际上 SessionStateItem 在属性上有一个脏标志,但没有在保存数据结束时进行此合并。如果其他人想为现有的 ms asp.net 会话状态保持器提供解决方案,我真的会很高兴,我正在写到目前为止我发现的东西,但这并不意味着肯定没有办法 - 我没有照原样找到了。

希望对大家有所帮助:)

自定义 SessionStateModule

在这个答案https://***.com/a/3660837/159270James 在编写自定义模块后说:我仍然无法相信 ASP.Net Session 的自定义实现会锁定整个请求的会话。

【讨论】:

优点(稍后会 +1),但它忽略了一个事实,即即使“锁定”的 ASP 会话也可以在多服务器环境中保持乐观。如果我需要 独占锁定,那么无论InProc 在单个服务器上还是分布在一个大农场中,我都需要它来工作。如果我不能绝对依赖独占锁定,那么我必须将所有内容都视为可能被并发调用覆盖/丢弃。 @Keith asp.net 保存在数据库中的唯一一个字段 - 只有一个二进制数据字段。一个保存所有键的二进制数据字段。这不能处于乐观并发控制下,因为一个数据被删除所有其余的,或者需要一个合并方法,或者每个键更改的锁,但是每个键更改的锁太慢,因为这里我们说是数据库类型锁。 是的,我明白了——这不仅仅是一个键值对的最后写入获胜,而是整个会话。它不会改变独占锁仍然不能保证独占锁的事实。每当我使用会话时,我都必须为乐观场景编写代码,那么为什么我不能将其设为默认值呢? @Keith 在那篇帖子之后,我制作了完全自定义的会话保持器。它花费我的时间比我花在寻找使 ms 工作的解决方案上的时间要少。缺点是这是完全自定义的解决方案,而不是像 asp.net 这样的通用解决方案。其余的都超级酷,实际上我按照你说的方式实现了。 这也是我最终所做的 - 这是我可以确定某些 .Net 组件不会尝试锁定会话或假定它是独占的唯一方法。

以上是关于完全替换 ASP.Net 的会话的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET 中的会话劫持对策

如何防止asp.net应用程序会话劫持?

如何终止会话或会话 ID (ASP.NET/C#)

了解 ASP.Net 会话生命周期

ASP.NET 中的会话超时

什么是 ASP.NET 中的默认会话超时?