ben*_*ree 34 asp.net-mvc session webforms httpsession
在ASP.NET站点中使用会话时,几乎在同一时间加载多个请求时会导致严重的延迟(500毫秒的倍数).
我们的网站专门为其SessionId使用会话.我们使用此密钥来查找具有用户信息等的db表.这似乎是一个很好的设计,因为它存储了最少的会话数据.不幸的是,如果你在会话中没有任何东西,SessionId会改变,所以我们存储Session["KeepId"] = 1;
.这足以说服SessionId不要改变.
在一个看似无关的说明中,该网站通过控制器操作提供定制的产品图像.如果需要,此操作会生成图像,然后重定向到缓存的图像.这意味着一个网页可能包含图像或其他资源,通过ASP.NET管道发送数十个请求.
无论出于何种原因,当您(a)同时发送多个请求并且(b)以任何方式涉及会话时,您将最终获得大约1/2时间的随机500ms延迟.在某些情况下,这些延迟将是1000毫秒或更长,总是以~500毫秒的间隔增加.更多请求意味着更长的等待时间 对于某些图像,我们包含数十张图像的页面可能需要等待10秒以上.
创建一个空的控制器动作:
public class HomeController : Controller
{
public ActionResult Test()
{
return new EmptyResult();
}
}
Run Code Online (Sandbox Code Playgroud)创建一个test.html页面,其中包含一些符合该操作的img标记:
<img src="Home/Test?1" />
<img src="Home/Test?2" />
<img src="Home/Test?3" />
<img src="Home/Test?4" />
Run Code Online (Sandbox Code Playgroud)运行页面并观察firebug中的快速加载时间:
执行以下操作之一(两者都具有相同的结果):
向Global.asax.cs添加一个空的Session_Start处理程序
public void Session_Start() { }
Run Code Online (Sandbox Code Playgroud)把东西放在会议中
public class HomeController : Controller
{
public ActionResult Test()
{
HttpContext.Current.Session["Test"] = 1;
return new EmptyResult();
}
}
Run Code Online (Sandbox Code Playgroud)再次运行该页面并注意响应中的偶然/随机延迟.
我如何使用会话但避免这些延迟?有谁知道修复?
如果没有修复,我想我们将不得不绕过会话并直接使用cookie(会话使用cookie).
ben*_*ree 21
正如Gats所提到的,问题是ASP.NET会锁定会话,因此同一会话的每个请求必须以串行方式运行.
令人讨厌的部分是如果我连续运行所有5个请求(从示例中),它需要大约40ms.使用ASP.NET锁定它超过1000毫秒.似乎ASP.NET说,"如果会话正在使用,那么睡眠500毫秒再试一次."
如果您使用StateServer或SqlServer而不是InProc,它将无济于事 - ASP.NET仍然会锁定会话.
有几个不同的修复程序.我们最终使用了第一个.
Cookie会在每个请求的标头中发送,因此您应该保持清晰并避免敏感信息.话虽这么说,会话默认使用cookie来记住谁是谁通过存储ASPNET_SessionId字符串.我所需要的就是id,所以没有理由忍受ASP.NET的会话锁定,因为它只是一个包含在cookie中的id的包装器.
所以我们完全避免会话并在cookie中存储guid.Cookie不会锁定,因此延迟是固定的.
您可以使用会话,但通过将会话设置为只读来避免对某些请求的锁定.
对于MVC 3应用程序,在控制器上使用此属性可将会话设置为只读(它不适用于特定操作):
[SessionState(SessionStateBehavior.ReadOnly)]
Run Code Online (Sandbox Code Playgroud)
您也可以通过MVC路由禁用会话,但它有点复杂.
对于WebForms,您可以禁用某些aspx页面的会话.
tig*_*rou 11
我看了一下ASP.NET框架.管理会话状态的类在此处定义:http://referencesource.microsoft.com/#System.Web/State/SessionStateModule.cs,114
以下是它的工作原理(简化代码):
LOCKED_ITEM_POLLING_INTERVAL = 500ms;
LOCKED_ITEM_POLLING_DELTA = 250ms;
bool GetSessionStateItem() {
item = sessionStore.GetItem(out locked);
if (item == null && locked) {
PollLockedSession();
return false;
}
return true;
}
void PollLockedSession() {
if (timer == null) {
timer = CreateTimer(PollLockedSessionCallback, LOCKED_ITEM_POLLING_INTERVAL);
}
}
void PollLockedSessionCallback() {
if (DateTime.UtcNow - lastPollCompleted >= LOCKED_ITEM_POLLING_DELTA) {
isCompleted = GetSessionStateItem();
lastPollCompleted = DateTime.UtcNow;
if(isCompleted) {
ResetPollTimer();
}
}
}
Run Code Online (Sandbox Code Playgroud)
摘要:如果由于另一个线程锁定了会话项而无法检索该会话项,则会创建一个计时器.它将定期池化会话以尝试再次获取项目(默认情况下每500毫秒).成功检索项目后,将清除计时器.此外,还要检查以确保GetSessionStateItem()调用之间存在给定的延迟(LOCKED_ITEM_POLLING_DELTA
默认情况下= 250 ms).
可以LOCKED_ITEM_POLLING_INTERVAL
通过在注册表中创建以下密钥来更改默认值(这将影响在计算机上运行的所有网站):
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET
SessionStateLockedItemPollInterval (this is a DWORD)
Run Code Online (Sandbox Code Playgroud)
另一种方式(这是一种黑客攻击)是通过反射来改变值:
Type type = typeof(SessionStateModule);
FieldInfo fieldInfo = type.GetField("LOCKED_ITEM_POLLING_INTERVAL",
BindingFlags.NonPublic | BindingFlags.Static);
fieldInfo.SetValue(null, 100); //100ms
Run Code Online (Sandbox Code Playgroud)
免责声明:降低此值的确切后果尚不清楚.它可能会增加服务器上的线程争用,创建潜在的死锁等...更好的解决方案是避免使用会话状态或用SessionStateBehavior.ReadOnly
其他用户建议的属性装饰您的控制器.
归档时间: |
|
查看次数: |
7597 次 |
最近记录: |