Asp.net mvc 在ASP.NET会话中存储任何内容都会导致500毫秒的延迟 问题是:

Asp.net mvc 在ASP.NET会话中存储任何内容都会导致500毫秒的延迟 问题是:,asp.net-mvc,session,webforms,httpsession,Asp.net Mvc,Session,Webforms,Httpsession,在ASP.NET站点中使用会话时,在几乎同时加载多个请求时,会导致显著的延迟(500毫秒的倍数) 更具体地说,是我的问题 我们的网站仅在SessionId中使用session。我们使用这个键来查找包含用户信息等的db表。这似乎是一个很好的设计,因为它存储了最少的会话数据。不幸的是,如果您在session中不存储任何内容,SessionId将发生变化,因此我们存储session[“KeepId”]=1。这就足够了 在一个看似无关的注释中,该网站通过控制器动作提供定制的产品图像。如果需要,此操作将生

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

更具体地说,是我的问题 我们的网站仅在SessionId中使用session。我们使用这个键来查找包含用户信息等的db表。这似乎是一个很好的设计,因为它存储了最少的会话数据。不幸的是,如果您在session中不存储任何内容,SessionId将发生变化,因此我们存储
session[“KeepId”]=1。这就足够了

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

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

如何重现问题:
  • 创建空的ASP.NET MVC Web应用程序
  • 创建空控制器操作:

    public class HomeController : Controller
    {
      public ActionResult Test()
      {
        return new EmptyResult();
      }
    }
    
  • 创建一个test.html页面,其中包含几个命中该操作的img标记:

    <img src="Home/Test?1" />
    <img src="Home/Test?2" />
    <img src="Home/Test?3" />
    <img src="Home/Test?4" />
    
  • 开会

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

  • 到目前为止我所知道的
    • MVC2&3和WebForms都会出现这种情况
    • 任何浏览器都会出现这种情况(我们尝试了FF、Chrome、IE、Safari)
    • 这是常有的事
    • 它发生在WebDev.exe、IIS Express和IIS上
    我的问题 如何使用会话但避免这些延迟?有人知道修复方法吗


    如果没有解决方案,我想我们将不得不绕过会话&直接使用cookies()。

    在ASP.NET中有几种方法可以加速会话。首先,有几个要点:

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

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

  • 因此,我最喜欢的加速会话请求的方法始终是使用状态服务器。它消除了过程中的问题,也意味着在测试中,您可以重建项目,而无需再次登录到您的站点。它还使会话在简单的负载平衡场景中成为可能。


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

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

    令人恼火的是,如果我以串行方式运行所有5个请求(来自示例),则需要大约40毫秒。有了ASP.NET的锁定,它的速度超过了1000毫秒。它似乎像ASP.NET所说,“如果会话正在使用中,则睡眠500毫秒并重试。”

    如果您使用StateServer或SqlServer而不是InProc,则不会有任何帮助—ASP.NET仍然会锁定会话

    有几种不同的修复方法。我们最后使用了第一个

    改用Cookies Cookie会在每个请求的标题中发送,因此您应该保持它的轻量级,避免敏感信息。也就是说,会话默认情况下使用cookies存储ASPNET_SessionId字符串来记住谁是谁。我所需要的只是那个id,所以当ASP.NET只是cookie中id的包装时,没有理由忍受它的会话锁定

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

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

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

    禁用某些路由的会话 你也可以,但有点复杂

    禁用特定页面上的会话
    对于WebForms,您可以。

    我只是想为@bendytree的伟大答案添加一点细节。使用WebForms时,不能仅完全禁用会话状态

    <%@ Page EnableSessionState="False" %>
    
    
    
    但也将其设置为只读:

    <%@ Page EnableSessionState="ReadOnly" %>
    
    
    
    这解决了我案例中描述的问题。
    请参见

    我查看了ASP.NET框架。此处定义了管理会话状态的类:

    下面是它的工作原理(简化代码):

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


    通过在注册表中创建以下项,可以更改
    LOCKED\u ITEM\u POLLING\u INTERVAL
    的默认值(这将影响计算机上运行的所有网站):

    另一个wa
    <%@ Page EnableSessionState="False" %>
    
    <%@ Page EnableSessionState="ReadOnly" %>
    
    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();
              }
        } 
    }
    
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET
    SessionStateLockedItemPollInterval (this is a DWORD)
    
    Type type = typeof(SessionStateModule);
    FieldInfo fieldInfo = type.GetField("LOCKED_ITEM_POLLING_INTERVAL",
       BindingFlags.NonPublic | BindingFlags.Static);
    fieldInfo.SetValue(null, 100); //100ms