Асинхронное изменение сессии в PHP

Автор: Denis Trubachev
Дата публикации: 2014-05-27 00:26:33

Изначально эта проблема появлялась при переходе на туже страницу в другой вкладке браузера, если скрипт выполнялся довольно долго и при том в скрипте необходима работа с сессией. Два запроса становятся в очередь и следующий выполнится только после предыдущего. Наглядный пример кода:

<?
  session_start();
  print "Run...
"; flush(); sleep(10); print "Done
"; ?>

Общее описание проблемы

Если последовательно открыть несколько вкладок, которые вызывают этот код, то функция session_start(); будет выполнена только в том случае, если больше нету скриптов, которые работают с сессией.

Проблема появляется из-за того, что движок сессий используемый в php по-умолчанию для каждой сессии создается свой файл, в которой храниться вся ее информация. Доступ к файлу получаем при вызове функции session_start . Если в данный момент файл используется, тогда ожидают освобождения файла сессии, и только потом скрипт продолжает выполнять свой код. Есть ещё один метод освобождения сессии – вызов метода session_close.

Проблема стала более распространена после большой популяризации AJAX запросов. На текущий момент контент большей части сайтов динамический и обновляется или заполняется с помощью AJAX.

Методы решения общей проблемы

В большей части форумов предлагают использовать функцию session_write_close(). пример статьи на эту тему. Данная функция позволяет закрывать сессию для записи, но считывание с сессии будет возможно. В некоторых случаях это решает проблему. Есть еще один вариант решения проблемы — хранить данные сессии в базе. Он больше подходит , т.к. предоставляет доступ к записям сессии. Хранение сессий в БД позволяет повысить производительность, т.к. при асинхронных запросах не наступает блокировка.

Есть все-таки то, что не решено

Но появилаяется следующая проблема: если пользователь находится в ожидании регистрации. скажем пользователь вносит данные в форму авторизации или регистрации, а в этот момент выполняется какой-то AJAX (скажем, какой-нибудь динамический список с автопрокруткой). После отправления данных с формы регистрации он должен успешно авторизоваться. Авторизованному пользователю, как известно, доступно больше контента, чем гостю, и наш случай не исключение. Этот контент генерируется скриптом регистрации. Думаю с кодом будет более понятно.

Код скрипта

<?
session_start(); //открытие сессии
echo 'AJAX response'; //формирование ответа
saveSessionData('latestRequestTime' => time()); //сохранение данных в сессию
?>

Если этот скрипт вызывать AJAX запросом сразу после загрузки страницы сайта. Скрипт регистрации, скажем будет иметь абстрактно такой код:

<?
session_start(); //открытие сессии
$user = register_user($_REQUEST); //регистрация нового пользователя
echo 'registration response'; //формирование ответа
saveSessionData('logged_user_id' => $user->id, 'latestRequestTime' => time()); //авторизация пользователя (записывает данные в сессию)
?>

И тут начинается веселие, если запись выполняется довольно долгое время, то так выглядит workflow:

Проблема заключается в том, что мы пытаемся записать в одну и ту же сессию разные данные в разные моменты времени. Неизвестно какое значение останется после выполнения, ведь запросы к серверу выполняются параллельно.

Решение проблемы

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

Если файл есть и время блокировки еще не истекло, то запись не осуществляется. Это решит проблему с одновременной записью в БД данных сессии. Так же решение позволит считывать всю необходимую информацию сессий из базы без задержек.

Описание реализации

При записи данных из сессии в базу, проверяется блокировка для сессии. Блокировка - есть наличие файла с названием сессии. Если блокировка есть, то запись сессии не происходит. Если в рамках данного скрипта мы пометили, что сессию стоит заблокировать на определенное время, то по окончанию его работы происходит следующее:

  1. Создается файл блокировки
  2. Данные текущей сессии записываются в БД

Итоги

Проблема достаточно популярна, и актуальность проблемы растет с масштабами проекта. Решают кто как может, один из вариантов решения предоставлено в этой статье.

В основном мы работаем с yii framework, то я выложил готовый компонент в extensions. Можно скачать здесь.

Буду рад если кому то помог:))

ссылки по теме:

Комментарии к статье
Комментарии:
Нет результатов.
Только зарегистрированые пользователи могут оставлять комментарии