【问题标题】:Why would multiple simultaneous AJAX calls to the same ASP.NET MVC action cause the browser to block?为什么对同一个 ASP.NET MVC 操作的多个同时 AJAX 调用会导致浏览器阻塞?
【发布时间】:2010-12-13 11:37:19
【问题描述】:

几天前我问了这个问题:

Why does $.getJSON() block the browser?

我几乎同时在同一个控制器操作中触发了六个 jQuery 异步 ajax 请求。每个请求需要 10 秒才能返回。

通过对 action 方法的调试和记录请求,我注意到请求是序列化的,并且永远不会并行运行。即,我在 log4net 日志中看到这样的时间线:

2010-12-13 13:25:06,633 [11164] 信息-得到:1156 2010-12-13 13:25:16,634 [11164] 信息-返回:1156 2010-12-13 13:25:16,770 [7124] 信息-得到:1426 2010-12-13 13:25:26,772 [7124] 信息-返回:1426 2010-12-13 13:25:26,925 [11164] 信息-得到:1912 2010-12-13 13:25:36,926 [11164] 信息-返回:1912 2010-12-13 13:25:37,096 [9812] 信息-得到:1913 2010-12-13 13:25:47,098 [9812] 信息-返回:1913 2010-12-13 13:25:47,283 [7124] 信息-得到:2002 2010-12-13 13:25:57,285 [7124] 信息-返回:2002 2010-12-13 13:25:57,424 [11164] 信息-得到:1308 2010-12-13 13:26:07,425 [11164] 信息-返回:1308

查看 FireFox 中的网络时间线,我看到了:

上面的日志示例和 Firefox 网络时间线都是针对同一组请求的。

是否对来自同一页面的同一操作的请求进行了序列化? 我知道在同一会话中对Session 对象的序列化访问,但没有触及会话数据。 p>

我将客户端代码剥离为单个请求(运行时间最长的请求),但这仍然会阻塞浏览器,即只有当 ajax 请求完成时,浏览器才会响应任何链接点击。

我在这里(在 Chrome 的开发人员工具中)还观察到的是,在执行长时间运行的 ajax 请求时单击链接时,它会立即报告 Failed to load resource 错误,这表明浏览器已终止(或正在尝试终止)并等待?)ajax请求:

但是浏览器仍然需要一段时间才能重定向到新页面。

ajax 请求真的是异步的,还是因为 javascript 实际上是单线程的,所以这是一种花招?

我的请求是否需要太长时间才能生效?

Firefox 和 IE 也会出现此问题。

我还将脚本更改为直接使用$.ajax,并显式设置async: true

我在 IIS7.5 上运行它,Windows 2008R2 和 Windows 7 版本都做同样的事情。

调试和发布版本的行为也相同。

【问题讨论】:

  • 你在 Firefox/IE 中测试过这个吗?也许是 chrome 问题?
  • @aseem - 在 F/Fox 和 IE 中是一样的
  • 也许你的脚本改变了其他地方的异步设置。
  • 发送多个请求时,您能否签入 FF/Firebug(Firebug 中的 Net Panel)。那些时间日志似乎不对,jquery ajax 请求肯定是异步的,必须并行运行。
  • @aseem - 请求标头中没有“发送”时间。我要说的是,因为它们都被一次请求,Firefox(或谷歌)无法按任何合理的顺序排序。存在的顺序是由于每个底层 XHR 响应并确认“是的,确实我已发送此请求”所花费的时间,因此由于制作 TCP/IP 的差异,每个 XHR 之间可能存在几毫秒的差异联系。因此出现了乱序的时间线。但是我知道事实上这些调用是序列化的(控制器操作中的日志记录告诉我这一点)......继续......

标签: javascript jquery asp.net-mvc ajax asp.net-mvc-2


【解决方案1】:

答案是盯着我的脸。

ASP.NET Session State Overview:

每个会话对 ASP.NET 会话状态的访问是独占的,这意味着如果两个不同的用户发出并发请求,则同时授予对每个单独会话的访问权限。但是,如果对同一个会话发出两个并发请求(通过使用相同的 SessionID 值),则第一个请求将获得对会话信息的独占访问权。第二个请求只有在第一个请求完成后才会执行。

令人讨厌的是,几周前我浏览了这一段,并没有真正考虑到粗体句子的全部影响。我读到的只是“对会话状态的访问是序列化的”,而不是“所有请求,无论您是否触摸会话状态,都被序列化”来自同一个会话。

幸运的是,在 ASP.NET MVC3 中有一种变通方法,它可以创建无会话控制器。 Scott Guthrie 在这里谈到了这些:

Announcing ASP.NET MVC 3 (Release Candidate 2)

我安装了 MVC3 RC2 并升级了项目。用[SessionState(SessionStateBehavior.Disabled)]装饰有问题的控制器可以解决问题。

当然,我通常是几分钟前在 Stack Overflow 中发现的:

Asynchronous Controller is blocking requests in ASP.NET MVC through jQuery

【讨论】:

  • 谢谢。我遇到了同样的问题。
  • 感谢您对一个棘手的错误的帮助!是我自己还是这种明显愚蠢的默认行为?
  • 请注意,使用[SessionState(SessionStateBehavior.Disabled)] 可能会在您的应用程序中打开安全漏洞,例如在多个同时请求中访问私有数据,使用此选项您无法知道用户是否已登录,或者是否会话处于活动状态,等等...如果您需要访问会话,您可以使用[SessionState(SessionStateBehavior.ReadOnly)],因此您仍然可以访问您的会话,尽管您无法编辑它,但在许多情况下仍然可能会有所帮助。
  • @GregoireD。 - 感谢您提供有关SessionStateBehavior.ReadOnly 的信息。我有一种感觉,我看过它,它仍然会导致对您的Session 的序列化访问。该应用程序使用表单身份验证和 cookie/db 查找组合来锁定对该控制器的访问。从控制器本身返回的数据不是机密的,但我们确实采取了措施来防止人们出于好奇而摆弄。
  • SessionStateBehaviour.ReadOnly 在我的情况下似乎有效,并且仍然允许访问 Session。
【解决方案2】:

我试图重现这一点,但无法重现。这是我的测试:

private static readonly Random _random = new Random();

public ActionResult Ajax()
{
    var startTime = DateTime.Now;
    Thread.Sleep(_random.Next(5000, 10000));
    return Json(new { 
        startTime = startTime.ToString("HH:mm:ss fff"),
        endTime = DateTime.Now.ToString("HH:mm:ss fff") 
    }, JsonRequestBehavior.AllowGet);
}

还有电话:

<script type="text/javascript" src="/scripts/jquery-1.4.1.js"></script>
<script type="text/javascript">
    $(function () {
        for (var i = 0; i < 6; i++) {
            $.getJSON('/home/ajax', function (result) {
                $('#result').append($('<div/>').html(
                    result.startTime + ' | ' + result.endTime
                ));
            });
        }
    });
</script>

<div id="result"></div>

结果:

13:37:00 603 | 13:37:05 969
13:37:00 603 | 13:37:06 640
13:37:00 571 | 13:37:07 591
13:37:00 603 | 13:37:08 730
13:37:00 603 | 13:37:10 025
13:37:00 603 | 13:37:10 166

还有 FireBug 控制台:

如您所见,AJAX 操作是并行命中的。


更新:

在我的初始测试中,使用$.getJSON() 时,请求确实在 FireFox 3.6.12 和 Chrome 8.0.552.215 中排队。它在 IE8 中运行良好。我的测试是使用 ASP.NET MVC 2 项目、VS2010、Cassini Web 服务器、Windows 7 x64 位执行的。

现在,如果我将$.getJSON() 替换为$.get(),它在所有浏览器下都可以正常工作。这让我相信这个$.getJSON() 可能会导致请求排队。也许更熟悉 jQuery 框架内部的人能够更清楚地了解这个问题。


更新 2:

尝试设置cache: false

$.ajax({
    url: '/home/ajax', 
    cache: false,
    success: function (result) {
        $('#result').append($('<div/>').html(
            result.startTime + ' | ' + result.endTime
        ));
    }
});

【讨论】:

  • @Kev,是的,这有点奇怪。您可以尝试用$.get() 替换$.getJSON() 吗?我用卡西尼测试了这个。我没有安装 IIS,无法验证。
  • @darin - 我试过$.getJSON(),然后直接去了$.ajax(),结果是一样的。
  • 在 IE8 中运行这个示例对我来说很好。但是在 FF 和 Chrome 中,请求不是并行发送的。我正在使用 cassini 和 VS2010。
  • @uvita,用$.get() 替换$.getJSON() 有什么不同吗?对我来说,这很重要。使用$.getJSON 时,请求似乎已排队。
  • @darin,不,它没有。每个浏览器的结果完全相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-16
  • 2016-08-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多