我刚刚发现ASP中的每个请求。网络web应用程序在请求开始时获得一个会话锁,然后在请求结束时释放它!
如果你不明白这其中的含义,就像我一开始一样,这基本上意味着:
Any time an ASP.Net webpage is taking a long time to load (maybe due to a slow database call or whatever), and the user decides they want to navigate to a different page because they are tired of waiting, they can't! The ASP.Net session lock forces the new page request to wait until the original request has finished its painfully slow load. Arrrgh.
Anytime an UpdatePanel is loading slowly, and the user decides to navigate to a different page before the UpdatePanel has finished updating... they can't! The ASP.Net session lock forces the new page request to wait until the original request has finished its painfully slow load. Double Arrrgh!
那么有什么选择呢?到目前为止,我想出了:
Implement a Custom SessionStateDataStore, which ASP.Net supports. I haven't found too many out there to copy, and it seems kind of high risk and easy to mess up.
Keep track of all requests in progress, and if a request comes in from the same user, cancel the original request. Seems kind of extreme, but it would work (I think).
Don't use Session! When I need some kind of state for the user, I could just use Cache instead, and key items on the authenticated username, or some such thing. Again seems kind of extreme.
我真不敢相信ASP。Net微软团队在4.0版本的框架中留下了如此巨大的性能瓶颈!我是不是遗漏了什么明显的东西?为会话使用ThreadSafe集合有多难?
好的,非常感谢Joel Muller的贡献。我的最终解决方案是使用自定义SessionStateModule在这篇MSDN文章的最后详细说明:
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx
这是:
非常快地实现(实际上似乎比走提供者路线更容易)
使用了很多标准的ASP。开箱即用的网络会话处理(通过SessionStateUtility类)
This has made a HUGE difference to the feeling of "snapiness" to our application. I still can't believe the custom implementation of ASP.Net Session locks the session for the whole request. This adds such a huge amount of sluggishness to websites. Judging from the amount of online research I had to do (and conversations with several really experienced ASP.Net developers), a lot of people have experienced this issue, but very few people have ever got to the bottom of the cause. Maybe I will write a letter to Scott Gu...
如果您的页面没有修改任何会话变量,您可以选择退出此锁的大部分。
<% @Page EnableSessionState="ReadOnly" %>
如果您的页面不读取任何会话变量,您可以选择完全退出该页面的锁。
<% @Page EnableSessionState="False" %>
如果没有页面使用会话变量,只需在web.config中关闭会话状态。
<sessionState mode="Off" />
我很好奇,如果“一个ThreadSafe集合”不使用锁,你认为它会做什么来成为线程安全的?
Edit: I should probably explain by what I mean by "opt out of most of this lock". Any number of read-only-session or no-session pages can be processed for a given session at the same time without blocking each other. However, a read-write-session page can't start processing until all read-only requests have completed, and while it is running it must have exclusive access to that user's session in order to maintain consistency. Locking on individual values wouldn't work, because what if one page changes a set of related values as a group? How would you ensure that other pages running at the same time would get a consistent view of the user's session variables?
如果可能的话,我建议您在设置会话变量之后尽量减少对它们的修改。这将允许您将大部分页面设置为只读会话页面,从而增加来自同一用户的多个同时请求不会相互阻塞的机会。
我准备了一个基于这个线程中发布的链接的库。它使用了来自MSDN和CodeProject的例子。感谢詹姆斯。
我还在Joel Mueller的建议下做了一些修改。
代码在这里:
https://github.com/dermeister0/LockFreeSessionState
散列表模块:
Install-Package Heavysoft.LockFreeSessionState.HashTable
ScaleOut StateServer模块:
Install-Package Heavysoft.LockFreeSessionState.Soss
自定义模块:
Install-Package Heavysoft.LockFreeSessionState.Common
如果你想实现对Memcached或Redis的支持,安装这个包。然后继承LockFreeSessionStateModule类并实现抽象方法。
代码还没有在生产环境中进行测试。还需要改进错误处理。在当前实现中不捕获异常。
使用Redis的一些无锁会话提供程序:
https://github.com/angieslist/AL-Redis(由gregmac在本帖子中建议)
https://github.com/welegan/RedisSessionProvider (NuGet: RedisSessionProvider)
https://github.com/efaruk/playground/tree/master/UnlockedStateProvider (NuGet: UnlockedStateProvider.Redis)