Чому кілька одночасних викликів AJAX до однієї і тієї ж дії ASP.NET MVC спричиняють блокування браузера?


74

Кілька днів тому я задав це запитання:

Чому $ .getJSON () блокує браузер?

Я запускаю шість запитів jQuery async 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] ІНФО - Отримано: 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 запит:

текст заміщення

Однак браузеру все ще потрібен вік для перенаправлення на нову сторінку.

Чи справді запити Ajax є асинхронними, чи це спритність, оскільки javascript насправді є однопоточним?

Мої запити просто займають занадто багато часу, щоб це спрацювало?

Проблема виникає також у Firefox та IE.

Я також змінив сценарій для $.ajaxпрямого та явного встановлення async: true.

Я запускаю це на IIS7.5, як Windows 2008R2, так і Windows 7 роблять те саме.

Збірки налагодження та випуску також поводяться однаково.


Ви перевіряли це у Firefox / IE. Можливо, це проблема з хромом?
Aseem Gautam

@aseem - те саме у F / Fox та IE
Кев

Можливо, ваш скрипт де-небудь змінює налаштування асинхронізації.
ZippyV

Чи можете ви перевірити FF / Firebug, коли надсилаються кілька запитів (Net Panel у firebug). Ці часові журнали не здаються правильними, запити jquery ajax напевно є асинхронними і повинні виконуватися паралельно.
Aseem Gautam

1
@aseem - у заголовках запитів немає разів "відправлених". Я хочу сказати, що через те, що всі вони отримують запити відразу, firefox (або google) не може сортувати за будь-яким розумним порядком. Це розпорядження обумовлене часом, необхідним для відповіді кожного основного XHR і підтвердження того, що "так, я надіслав цей запит", тому різниця між ними може бути кілька мілісекунд через різницю у формуванні TCP / IP з'єднання. Звідси неробоча хронологія. Однак я точно знаю, що ці виклики серіалізовані (вхід в дію контролера говорить мені про це) ..... продовження ...
Кев

Відповіді:


90

Відповідь дивився мені в обличчя.

Огляд стану сеансу ASP.NET :

Доступ до стану сеансу ASP.NET є виключним для кожного сеансу, що означає, що якщо два різні користувачі роблять одночасні запити, доступ до кожного окремого сеансу надається одночасно. Однак, якщо для одного сеансу зроблено два одночасних запити (з використанням одного і того ж значення SessionID), перший запит отримує ексклюзивний доступ до інформації про сеанс. Другий запит виконується лише після завершення першого запиту.

Прикро, я пару тижнів тому прочитав цей абзац, насправді не враховуючи повного впливу сміливих речень. Я прочитав, що просто як "доступ до стану сеансу серіалізований", а не "всі запити, незалежно від того, торкаєтесь ви стану сесії, ні серіалізуються", якщо запити надходили з тієї самої сесії.

На щастя, в ASP.NET MVC3 є обхід, і його можна створити безсесійні контролери. Скотт Гатрі розповідає про це тут:

Анонс ASP.NET MVC 3 (випуск 2)

Я встановив MVC3 RC2 і модернізував проект. Оздоблення контролера, про який йде мова, [SessionState(SessionStateBehavior.Disabled)]вирішує проблему.

І звичайно, я зазвичай знайшов це у Stack Overflow кілька хвилин тому:

Асинхронний контролер блокує запити в ASP.NET MVC через jQuery


Дякую за це. У мене була така сама проблема.
Нік Олсен,

Дякуємо за допомогу з виправленою помилкою! Це лише я, чи це надзвичайно дурна поведінка за замовчуванням?
theycallmemorty

6
Зверніть увагу, що використання [SessionState(SessionStateBehavior.Disabled)]може відкрити порушення безпеки у вашій програмі, наприклад, доступ до приватних даних у декількох одночасних запитах, за допомогою цієї опції ви не можете знати, чи зареєстрований користувач, а також чи активний сеанс тощо ... Якщо ви Вам потрібен доступ до сеансу, яким ви можете скористатися [SessionState(SessionStateBehavior.ReadOnly)], тож у вас все ще є доступ до сеансу, хоча ви не можете редагувати його, але все одно може допомогти у багатьох випадках.
Грегуар Д.

1
@GregoireD. - Дякую за інформацію про SessionStateBehavior.ReadOnly. У мене відчуття, що я подивився на це, і що воно все ще призводить до серіального доступу до вашого Session. Додаток використовує автентифікацію форм та комбінований пошук файлів cookie / db для блокування доступу до цього контролера. Дані, що повертаються від самого контролера, не є конфіденційними, але ми вжили заходів, щоб люди не пустували з простою цікавості.
Кев

1
SessionStateBehaviour.ReadOnly, здається, працює в моєму випадку і все ще дозволяє доступ до сеансу.
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, Windows 7 x64 bit.

Тепер, якщо я заміню $.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 - я спробував $.getJSON()і пішов відразу, $.ajax()і результати однакові.
Кев

Запуск цього зразка в IE8 мені добре підходить. Однак у FF та Chrome запити не надсилаються паралельно. Я використовую кассіні та VS2010.
uvita

@uvita, чи має значення заміна $.getJSON()на $.get()щось? Для мене це має значення. При використанні $.getJSONзапити здаються в черзі.
Дарін Димитров

@darin, ні, цього не було. Точно однакові результати в кожному браузері.
uvita
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.