非锁定进程内 ASP.NET 会话状态存储

Posted

技术标签:

【中文标题】非锁定进程内 ASP.NET 会话状态存储【英文标题】:A non-locking in-process ASP.NET session state store 【发布时间】:2011-04-09 15:44:03 【问题描述】:

我正在使用 ASP.NET 的进程内会话状态存储。它以独占方式锁定对会话的访问,这意味着对同一会话的并发请求将按顺序提供。

我想移除这个隐式排他锁,这样每个会话的多个请求可以同时处理。当然,我会在适用的情况下自己同步对会话状态的访问。

我正在使用MSDN documentation of Session State Providers 编写我自己的会话状态提供程序,this SO question 向我指出 this example code 将其实现为 HTTP 模块,但代码看起来非常复杂,只是为了删除锁。

我可能最终应该使用 ASP.NET 的缓存来实现会话状态,并停止使用内置会话,就像 Vivek 在 this post 中描述的那样,但现在我想如何移除锁定。

有任何想法或示例实现吗?

【问题讨论】:

【参考方案1】:

不是您要寻找的答案,但我认为即使有可能,以这种方式更改 SessionState 的工作方式也是一个糟糕的主意。

想想那些不得不维护你的代码的可怜人。 Session 以这种方式序列化请求的事实意味着 ASP.NET 开发人员通常不需要过多担心线程安全性。

此外,如果有人添加了一个碰巧使用 Session 的第三方组件,它会期望通常的关于锁的保证 - 你会突然开始得到 Heisenbugs。

相反,请衡量性能并确定您需要同时处理请求的特定区域 - 我敢打赌,它们中的数量很少 - 并仅为所涉及的特定项目仔细实施您自己的锁定机制 - 可能是您想要的解决方案计划最终使用 ASP.NET 缓存。

【讨论】:

这是一个有效的答案,谢谢。任何可怜的人(可能是我 :) 都必须处理引入的任何类型的复杂性以添加功能,例如处理并发请求的能力。我们已经有一些(非正式的)使用会话的严格政策,因为假设无状态让我们的事情变得更简单。我们实际上假设在我们使用会话时没有请求锁定,并发现何时没有同时处理请求。 乔 - 喜欢你的回复。由 3rd-party 组件做出的假设是一个很好的观点,并且更改会话的行为可能会违反该合同。谢谢。 @orip 我不同意 - 我有一个非常相似的问题 (***.com/questions/8989648) 但发现除非你可以确定你只需要支持 InProc 会话然后这就是问题你总是要考虑的。 SQL 服务器会话用于网络农场(其中基于进程的不是一个选项),它们在同一台服务器上是独占的,但如果在服务器之间共享,则切换到乐观。这意味着我们有一些具有排他行为的客户(锁定在一台服务器上),而另一些则是最后写入的赢家。我们花了很长时间才找到这方面的问题。 如果我需要保证任何数据项的独占并发,那么 ASP 会话不是放置它的地方。 @Keith - 我想我同意你的看法。我认为放弃 ASP.NET 的内置会话并显式使用内存、某些键值存储或 SQL 意味着更少的隐含限制或意外行为,并且从长远来看更明智。不过,Joe 关于其他组件的期望的观点仍然有效。【参考方案2】:

我建议您考虑将会话移至 SQL Server Mode。 SQL Server 会话允许来自多个 Web 服务器的任意数量的请求共享一个会话。据我所知,SQL Server 在内部管理锁定机制,效率极高。您还有一个额外的好处是内存占用更少。而且你不需要编写一个 HttpModule 来运行它。

【讨论】:

您确定会话状态在 SQL Server 模式下也没有按请求锁定吗? @orip:是的,我一直认为锁定机制是由 SQL Server 事务自然管理的。但是我查看了这个页面msdn.microsoft.com/en-us/library/ms178587.aspx,它似乎仍然在网络服务器级别处理。【参考方案3】:

但仅出于解除锁定的目的,代码看起来非常复杂。

那是因为您将其视为某人偶然/或懒惰添加的简单锁,而不是整个会话提供程序概念中考虑的因素。对于手头的任务,该代码看起来很简单/用您自己的/替换整个现有提供程序/同时以不同于预期的方式锁定。

我建议您阅读更多关于会话概念如何在 asp.net 中工作的内容。考虑一下它的设计方式包括在请求中读取整个会话一次,然后写入一次更改的会话。

另请注意,虽然它不是您的场景,但代码可能依赖于读取单独的会话值来处理某些内容,并且也可以写入单独的值/单独锁定可以让您进入可能导致数据库死锁的相同考虑因素。

在加载/存储每个单独的项目时,设计的方式与您的读/写+锁定意图形成鲜明对比。

另请注意,您可能正在更新从会话中检索到的对象的值,而不是将其设置回该对象,但会话已更新。据我所知,提供者会在生命周期的某个时间点将会话中的所有内容写回,因此如果您想在项目级别分离读锁和写锁,这并不简单。

最后,如果您发现框架会话提供程序模型的阻力很高,并且您打算修改它的方式将阻止您使用它的某些功能(如切换到差异提供程序,因为那些会具有同样的问题);那么我认为您应该远离它,并根据您的特定需求推出自己的产品。这甚至不需要使用 asp.net 缓存,因为您希望它成为您的方式,您可以控制存储在其中的内容的生命周期和锁定。

在我看来,您完全需要一种不同的机制,所以我看不到尝试弯曲框架来这样做的好处。您可能会重复使用一些非常具体的部分,例如生成会话 id / 和 cookie 与无 cookie,但除此之外,它是一个不同的野兽。

【讨论】:

@RPM1984 在那里,没有更多的 在那里,有时它会接管:~【参考方案4】:

您必须编写自己的会话状态提供程序,这并不像看起来那么难。我不建议您使用缓存,因为正如 Darin 所说,缓存不够可靠,例如,当内存不足时,那里的项目过期和/或从缓存中删除。

在您的 Session Store Provider 中,您可以在编写时锁定会话的 items 而不是整个 Session 状态(这适合我们的项目),这是基本代码应该在ISessionStateItemCollection 的实现中(在索引器中),如下所示:

public object this[string name]

    get
    
        //no lock, just returning item (maybe immutable, it depends on how you use it)
    
    set
    
        //lock here
    

对于我们的项目,我们创建了将项目存储在 AppFabric 缓存中并依赖缓存的 GetAndLock、PutAndUnlock 方法的实现。如果你有进程内会话,那么你可能只需要lock(smthg)

希望这会有所帮助。

【讨论】:

谢谢,所以我不是唯一需要这个的疯子 :) 锁定是您编写自己的会话状态提供程序的唯一原因吗? 是的,当只有 2-3 行真正需要锁定时,厌倦了整个请求被锁定:) 我使用了来自 MSDN 的示例代码和文档,我的回答中显示了锁定每个项目而不是整个会话的想法。我还认为您已经决定不采用这种解决方案:) 从长远来看,我想做的是一件事,下周我要做的是另一件事 :) 我认为 MSDN 的示例代码和文档令人困惑,所以另一个例子对我有帮助很多:)【参考方案5】:

如果您只是从给定页面上的会话中读取,您可以使用 Page 指令:

<%@ Page EnableSessionState="ReadOnly" %>

表示只读性质并删除排他写锁,这将允许从同一会话并发请求到该页面。

由于内部使用了 ReaderWriter 锁,因此读取器锁将阻塞写入器锁,但读取器锁不会阻塞读取器锁。写锁会阻塞所有读写锁。

如果您需要从同一页面读取和写入会话,我认为您已经找到的信息已经足够了。只是关于用缓存替换会话的评论:虽然会话是可靠的,但缓存并不意味着如果您将某些内容放入缓存中,则不能保证将其取回。 ASP.NET 可能会在某些情况下(例如内存压力低)决定驱逐缓存,因此您始终需要在访问之前检查缓存中是否存在项目。

【讨论】:

谢谢达林。 ReadOnlyReadWrite 并没有解决我的问题,因为两者都锁定了整个会话(即使它只是一个读锁),而且我希望能够根据特定的会话密钥锁定基础。另一方面,InProcSessionStateStore 将其状态保存在 ASP.NET 缓存中,它不会遇到与直接使用缓存相同的问题吗? @orip,根据this article:For pages that do not update session state, you can indicate that read-only access is required. This does not prevent the state data from being fetched, but it results in a read lock being taken on the database, which enables multiple read-only requests to access the session simultaneously and prevents lock contention when multiple requests are made to the server with the same session ID。所以ReadOnly 将允许对该页面的并发请求。 它们将允许并发读取请求,但单个写入请求将阻止所有其他请求。就我而言,我不希望写入请求阻止任何与正在更新的会话值无关的请求。

以上是关于非锁定进程内 ASP.NET 会话状态存储的主要内容,如果未能解决你的问题,请参考以下文章

在 Ajax 调用期间保持 ASP.Net 会话处于活动状态

扩展 ASP.NET 会话状态服务器

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

ASP.NET Session详解

无法向会话状态服务器发出会话状态请求请。确保 ASP.NET State Service (ASP.NET 状态服务)已启动

ASP.Net 会话状态提供程序故障转移方案