Рубрики
Разработка

Подвисание веб-приложения после прекращения Ajax-запроса и сессии PHP/ASP.NET

Столкнулся на днях с проблемой подвисания веб-приложения, после прерывания Ajax-запроса на сервер. Смотрю в firebug’е — запрос прерывается, пытаюсь перейти на другую страницу — не могу, жду… Полез разбираться, оказалось дело в PHPшной сессии.

Ну да чтобы было понятно что к чему, для начала скажу о исходных данных. В веб-приложении есть некий диаложик, где можно указать айпишник и порт для подключения некоего нового сервиса. После ввода этих данных можно нажать кнопку «Тест», по которой эти данные улетают аяксом на сервер, где некий PHPшный скрипт тупо ломится curl’ом на указанный порт указанного айпишника и пытается получить оттуда определенный ответ. В это время пользователю, чтобы он не скучал, показывается очередной выпуск Hot for Words бегунок, что мол идет проверка, и кнопка «Прервать», по нажатию на которую, что вполне логично, эта проверка должна быть отменена. Так вот, браузер честно прерывает соединение с сервером и готов к последующим действиям пользователя, однако все попытки перейти на другие страницы или выполнить другие запросы к серверу выполняются с огромными задержками. Ах да, забыл сказать, в скрипте-обработчике присутствует присутствовал вызов session_start().

Что же является причиной подвисания веб-приложения?

Все дело в том, что браузер прерывает соединение с сервером — это да, но скрипт-обработчик, который пытается достучаться до несуществующего адреса все еще выполняется, а поскольку в нем открывается PHPшная сессия, то все последующие запросы, в которых так же идет работа сессией, ждут завершения этой.

Решений у этой проблемы как минимум несколько:

  • Не использовать сессию во всех скриптах просто потому что так можно, а делать это только там, где это действительно необходимо. Однако данный подход не всегда можно успешно применить и может так получится, что будет несколько одновременно выполняющихся скриптов, которые работают с сессией. Поэтому есть другой вариант.
  • В начале скрипта прочитать данные из сессии в локальные переменные, затем закрыть сессию с помощью session_write_close ( void ), а уже потом спокойненько делать свои дела.

Все сокровенные знания были найдены в описании функции session_write_close ( void ). Дабы далеко не ходить, привожу их здесь:

void session_write_close ( void )
End the current session and store session data.
Session data is usually stored after your script terminated without the need to call session_write_close(), but as session data is locked to prevent concurrent writes only one script may operate on a session at any time. When using framesets together with sessions you will experience the frames loading one by one due to this locking. You can reduce the time needed to load all the frames by ending the session as soon as all changes to session variables are done.

Если верить интернету, то в ASP.NET существует аналогичная проблема, потому как все запросы на сервер, работающие с сессией так же как и в PHP сериализуются, и обработка следующего запроса не начинается, пока не заканчивается предыдущий. Решается это установкой IReadOnlySessionState вместо IRequiresSessionState, в этом случае сессия не блокируется, и параллельный запрос (не зависимо от использования сессии) успешно обрабатывается.