为什么对同一个ASP.NET MVC操作同时进行多个AJAX调用会导致浏览器阻塞?


74

几天前,我问了这个问题:

为什么$ .getJSON()会阻止浏览器?

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

通过调试和记录对操作方法的请求,我注意到请求已被序列化,并且从未并行运行。即我在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] INFO-Got: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在同一会话中对对象的序列化访问,但是没有任何会话数据被触摸。

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

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

替代文字

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

Ajax请求真的是异步的吗?还是因为JavaScript实际上是单线程的,所以这是个小技巧吗?

我的请求是否花了太长时间才能完成?

Firefox和IE中也会发生此问题。

我还更改了脚本以$.ajax直接和显式使用set async: true

我在IIS7.5上运行此代码,Windows 2008R2和Windows 7版本都执行相同的操作。

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


您是否在firefox / IE中对此进行了测试。也许是镀铬的问题?
Aseem Gautam,2010年

@aseem-在F / Fox和IE中是相同的
Kev 2010年

也许您的脚本在其他地方更改了异步设置。
ZippyV 2010年

发送多个请求时,是否可以签入FF / Firebug(firebug中的Net Panel)。这些时间日志似乎不正确,jquery ajax请求肯定是异步的,必须并行运行。
Aseem Gautam,2010年

1
@aseem-请求标头中没有“发送”时间。我的意思是,因为它们都被同时请求了,Firefox(或Google)无法按任何明智的顺序进行排序。存在的顺序是由于每个基础XHR响应并确认“是的,我确实已经发送了此请求”所花费的时间,因此由于制作TCP / IP的差异,每个之间可能会有几毫秒的差异连接。因此,时间顺序混乱。但是我知道这些调用是序列化的(控制器操作中的记录告诉我这一点)..... cont ...
Kev 2010年

Answers:


90

答案盯着我。

ASP.NET会话状态概述

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

令人讨厌的是,我在两周前略过了这段段落,但并没有真正接受大胆句子的全部影响。我已经读过,如果请求来自同一会话,则仅是“访问会话状态已序列化”,而不是“所有请求,无论您是否触摸会话状态,都已序列化”

幸运的是,ASP.NET MVC3中有一个解决方法,它可以创建无会话的控制器。Scott Guthrie在这里谈论这些:

宣布ASP.NET MVC 3(候选发布2)

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

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

异步控制器通过jQuery阻止ASP.NET MVC中的请求


谢谢你 我遇到了同样的问题。
尼克·奥尔森

感谢您提供的一个棘手的错误帮助!只是我还是这种愚蠢的默认行为?
称呼我叫死

6
请注意,使用[SessionState(SessionStateBehavior.Disabled)]可能会打开应用程序安全漏洞,例如在多个同时请求中访问私有数据,使用此选项,您将无法知道用户已登录,会话是否处于活动状态,等等。需要访问您可以使用的会话[SessionState(SessionStateBehavior.ReadOnly)],因此您仍然可以访问该会话,尽管您无法对其进行编辑,但是在许多情况下仍然可以提供帮助。
Gregoire D.

1
@GregoireD。-感谢您提供有关的信息SessionStateBehavior.ReadOnly。我觉得自己已经看过了,仍然会导致对您的的序列化访问Session。该应用程序使用表单身份验证以及cookie / db查找组合来锁定对该控制器的访问。从控制器本身返回的数据不是机密信息,但是我们确实采取了措施来防止人们出于好奇而闲逛。
凯夫

1
在我的情况下,SessionStateBehaviour.ReadOnly似乎可以正常工作,并且仍然允许访问Session。
Miika L.

9

我试图重现此内容,但无法进行。这是我的测试:

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操作是并行命中的。


更新:

看来,在我的初始测试中,使用时,请求确实在FireFox 3.6.12和Chrome 8.0.552.215中排队$.getJSON()。在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,是的,这有点奇怪。您可以尝试替换$.getJSON()$.get()吗?我用卡西尼号测试过。我没有安装IIS,无法验证。
Darin Dimitrov 2010年

@darin-我试着$.getJSON()直接去$.ajax(),结果是一样的。
凯夫

在IE8中运行此示例对我来说很好。但是,在FF和Chrome中,请求不是并行发送的。我正在使用卡西尼和VS2010。
uvita 2010年

@uvita,用替换$.getJSON()$.get()什么不同吗?对我而言,这有所作为。使用$.getJSON请求时似乎已排队。
Darin Dimitrov 2010年

@darin,不,不是。在每个浏览器中,结果完全相同。
uvita 2010年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.