39

问题:

当您在 ASP.NET 站点中使用会话时,几乎同时加载多个请求时会导致显着延迟(500 毫秒的倍数)。

更具体地说,我的问题

我们的网站将 session 专门用于其 SessionId。我们使用这个键来查找包含用户信息等的数据库表。这似乎是一个很好的设计,因为它存储了最少的会话数据。不幸的是,如果您在会话中不存储任何内容,则 SessionId 会发生变化,因此我们存储Session["KeepId"] = 1;. 这足以说服 SessionId 不改变

在看似无关的注释中,该站点通过控制器操作提供定制的产品图像。如果需要,此操作会生成图像,然后重定向到缓存的图像。这意味着一个网页可能包含图像或其他资源,通过 ASP.NET 管道发送数十个请求。

无论出于何种原因,当您 (a) 同时发送多个请求并且 (b) 以任何方式涉及会话时,您最终将随机延迟 500 毫秒,大约是 1/2 的时间。在某些情况下,这些延迟将达到 1000 毫秒或更长,并且总是以约 500 毫秒的间隔增加。更多请求意味着更长的等待时间。我们有几十张图片的页面可能会等待 10 多秒才能看到一些图片。

如何重现问题:

  1. 创建一个空的 ASP.NET MVC Web 应用程序
  2. 创建一个空的控制器动作:

    public class HomeController : Controller
    {
      public ActionResult Test()
      {
        return new EmptyResult();
      }
    }
    
  3. 制作一个 test.html 页面,其中包含一些执行该操作的 img 标签:

    <img src="Home/Test?1" />
    <img src="Home/Test?2" />
    <img src="Home/Test?3" />
    <img src="Home/Test?4" />
    
  4. 运行页面并在 firebug 中观察快速加载时间:

    每个图像的加载速度都相当快

  5. 执行以下操作之一(两者的结果相同):

    • 将一个空的 Session_Start 处理程序添加到您的 Global.asax.cs

      public void Session_Start() { }
      
    • 在会话中放置一些东西

      public class HomeController : Controller
      {
        public ActionResult Test()
        {
          HttpContext.Current.Session["Test"] = 1;
          return new EmptyResult();
        }
      }
      
  6. 再次运行该页面并注意响应中偶尔/随机的延迟。

    一些请求经历了长时间的延迟

到目前为止我所知道的

我的问题

如何使用会话但避免这些延迟?有谁知道修复?

如果没有修复,我想我们将不得不绕过会话并直接使用 cookie(会话使用 cookie)。

4

4 回答 4

23

正如 Gats 所提到的,问题在于 ASP.NET 锁定会话,因此对同一会话的每个请求都必须串行运行。

烦人的部分是,如果我串行运行所有 5 个请求(来自示例),则需要大约 40 毫秒。使用 ASP.NET 的锁定时间超过 1000 毫秒。似乎 ASP.NET 说:“如果会话正在使用,则休眠 500 毫秒并重试。”

如果您使用 StateServer 或 SqlServer 而不是 InProc,它将无济于事 - ASP.NET 仍会锁定会话。

有几个不同的修复。我们最终使用了第一个。

改用 Cookie

Cookie 会在每个请求的标头中发送,因此您应该保持简洁并避免敏感信息。话虽如此,会话默认使用 cookie 通过存储 ASPNET_SessionId 字符串来记住谁是谁。我所需要的只是那个 id,所以当它只是一个 cookie 中的 id 的包装器时,没有理由忍受 ASP.NET 的会话锁定。

所以我们完全避免了会话,而是在 cookie 中存储了一个 guid。Cookie 不会锁定,因此延迟是固定的。

使用 MVC 属性

您可以使用会话,但通过将会话设置为只读来避免锁定某些请求。

对于 MVC 3 应用程序,请在控制器上使用此属性以使会话只读(它不适用于特定操作):

[SessionState(SessionStateBehavior.ReadOnly)]

禁用某些路由的会话

也可以通过 MVC 路由禁用会话,但是有点复杂。

在特定页面上禁用会话

对于 WebForms,您可以禁用某些 aspx pages 的会话

于 2011-12-02T16:01:29.730 回答
13

我看了一下 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();
          }
    } 
}

摘要:如果会话项由于被另一个线程锁定而无法检索,则会创建一个计时器。它将定期汇集会话以尝试再次获取项目(默认情况下每 500 毫秒)。成功检索项目后,计时器将被清除。此外,还有一项检查以确保 GetSessionStateItem() 调用之间存在给定的延迟(LOCKED_ITEM_POLLING_DELTA默认情况下 = 250 毫秒)。


可以通过在注册表中创建以下键来更改默认值LOCKED_ITEM_POLLING_INTERVAL(这将影响机器上运行的所有网站):

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET
SessionStateLockedItemPollInterval (this is a DWORD)

另一种方法(这是一种 hack)是通过反射改变值:

Type type = typeof(SessionStateModule);
FieldInfo fieldInfo = type.GetField("LOCKED_ITEM_POLLING_INTERVAL",
   BindingFlags.NonPublic | BindingFlags.Static);
fieldInfo.SetValue(null, 100); //100ms

免责声明:降低此值的确切后果未知。它可能会增加服务器上的线程争用,产生潜在的死锁等......更好的解决方案是避免使用会话状态或SessionStateBehavior.ReadOnly像其他用户建议的那样使用属性装饰您的控制器。

于 2015-09-18T08:06:42.183 回答
2

有几种方法可以加速 ASP.NET 中的会话。首先是几个关键点:

  1. 会话是线程安全的,这意味着它不能很好地处理需要访问它的并发客户端请求。如果您需要异步进程来访问会话类型的数据,您可以使用其他形式的临时存储,例如缓存或数据库(对于异步进度跟踪,这是必要的)。否则,用户会话信息将锁定请求的上下文,以防止更改它时出现问题。因此,为什么请求 1 很好,那么以下请求会有延迟。PS 这对于保证会话中数据的完整性是绝对必要的……就像电子商务中的用户订单一样。

  2. 会话在服务器上被序列化进出。cookie 用于维护会话,尽管它是一种不同类型的 cookie,这意味着我不希望性能有太大差异。

所以我最喜欢的加速会话请求的方法总是使用状态服务器。它消除了进程中的问题,也意味着在测试中您可以重建您的项目,而无需再次登录到您的站点。它还使会话在简单的负载平衡场景中成为可能。 http://msdn.microsoft.com/en-us/library/ms972429.aspx

PS我应该补充一点,从技术上讲,进程状态服务应该更慢。根据我的经验,使用它进行开发更快、更容易,但我想这取决于它的使用方式。

于 2011-12-01T22:27:14.330 回答
1

我只是想为@bendytree 的出色答案添加一些细节。使用 WebForms 时,您不能只完全禁用会话状态

<%@ Page EnableSessionState="False" %>

但也将其设置为只读:

<%@ Page EnableSessionState="ReadOnly" %>

这解决了我所描述的问题。查看文档

于 2015-06-11T09:02:31.540 回答