Блокирование сессий в PHP
21 июня 2012
Как-то я потратил довольно много времени, пытаясь понять, почему десяток асинхронных запросов к PHP-скрипту выстраивается в очередь вместо того, чтобы отрабатывать параллельно.
Чтобы показать наглядно, набросаем пару тестовых скриптов.
test1.php
session_start(); sleep(10); echo '1';
test2.php
session_start(); echo '2';
Ничего необычного, правда?
Теперь запустим их параллельно. Воспользуемся jQuery:
$.get('/test1.php'); $.get('/test2.php');
В итоге получается вот такая картинка (это вкладка Net из Firebug):
test1.php
заблокировал работу test2.php
.
При использовании сессий «из коробки», данные хранятся в одном единственном файле, который оказывается заблокированным с момента вызова session_start
и до окончания работы скрипта.
В том случае, если сессия вам нужна только для чтения, или есть возможность записать всё необходимое перед медленной частью скрипта, можно её закрыть явно при помощи session_write_close()
:
session_start(); session_write_close(); sleep(10); echo '1';
В этом случае мы получим желаемую картину:
Если же необходимо писать в сессию после медленных операций, придётся сменить «коробочную» сессию на свою реализацию с неблокируемым хранилищем, таким, например, как база данных.
Стоит отметить, что если при этом не позаботится о race condition, можно наступить на хорошие такие грабли.
Материалы:
Комментарии RSS по email OK
Правильно ли я понимаю что ниже можно опять открыть сессию и продолжить работу, при этом если несколько параллельных скриптов будут с сессией работать, то получится абракадабра в итоге (данные будут сливаться в одну сессию со всех скриптов по принципу кто последний записал, то и будет)?
Из доков по пхп: 2Артем, 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.
Артём Курапов, да, но открывать повторно не стоит не только по этой причине. При повторном открытии сесси в рамках одного запроса засоряется cookie и, в конечном итоге, убивается один известный браузер.
Собственно, правила простые: прочёл из сессии — закрыл, записал в сессию — снова закрыл. Сильно ускоряет отзывчивость сайта.
Ни с какими «известными браузерами» проблем при этом нет.
Разве? https://bugs.php.net/bug.php?id=38104
Sam Вообще да, Вы отчасти правы — я посмотрел у себя, действительно, каждый session_start вызывает выдачу новой куки. Но у меня не больше 2-3 таких операций на странице, так что проблем с браузерами это не вызывает.
Если же таких операций много, то надо придумывать обходные пути. Например, собирать все данные для записи в какой-то массив, а потом, по окончании отработки запроса, записывать их в сессию за один раз.
Ты можно сказать везунчик - столько в разработке и только вот наткнулся на эту фигню?
Да, весёлая штука такая, многих ставит в тупик. Интересно, в Zend Certification нет ли такого вопроса. Очень каверзный был бы вопрос :)
Psih, на самом деле наткнулся уже очень давно. Просто написал только сейчас. В Zend Certification мне не попадался и при подготовке тоже не всплывал.
Проверил локально. Второй скрипт вернулся сразу же (5мс), первый ждал 10.01. php5.3, apache2.2, xubuntu. В конфигах ничего специфического не трогал. Однако пока писал авторизацию вручную не раз удалял сессии, и заметил что там далеко не 1 файл, а столько - ско$.get('/test1.php'); $.get('/test2.php');лько сессий.
проблема довольно известная и решается переводом сессий на memcached или базу... через memcached делается буквально в одну строчку кода...
Виталий, это если не заботится о race condition.
Никто еще не написал extension для Yii ? перевод сессий в mysql.
извиняюсь CDbHttpSession уже есть
Стоит так же упомянуть, что хранение данных в сессиях, кроме самых простых случаев, не самое лучшее архитектурное решение, ограниченное в управляемости. Имхо, сессиям стоит оставить только id текущего пользователя.