Как устроены сессии в PHP, как реализован данный механизм в MODX Revolution и как его можно улучшить

Работая над оптимизацией сайта проекта Лектория, я всегда ищу узкие места, которые в перспективе могут привести к существенным проблемам и стараюсь их в максимально короткие сроки закрывать. Одним из таких вопросов, с которыми я недавно столкнулся, оказался вопрос работы стандартного механизма обработки сессий в MODX.

Автор материала

Артем Зернов. Веб-разработчик, создатель проекта Лектория, эксперт MODX Revolution, директор веб-студии OpenColour. Youtube-канал OpenModx.

126
8 минут на прочтение
Теги по этой теме:

Как я начал ковыряться в этом вопросе

Меня зовут Артем Зернов. Я веб-разработчик, занимаюсь развитием проекта Лектория, а также веду свой youtube-канал OpenModx, а еще у меня есть веб-студия OpenColour, в которой мы создаем сайты, в основном на MODX Revolution. В процессе своей деятельности в нашей команде мы создали не одну сотню сайтов, и в процессе их обслуживания я замечал, что некоторые, наиболее посещаемые сайты начинали тормозить. Причиной тому была таблица modx_session, о которой мы поговорим позже. Столкнувшись с этой проблемой, я начал разбираться, как устроен механизм сессий в PHP и MODX в частности. И вот буквально сегодня я вновь вернулся к этому вопросу, анализируя поведение сайта Лектории. Спешу поделиться своими наблюдениями и идеями.

Как устроены сессии в PHP по-умолчанию

При посещении какой-либо веб-страницы сайта, созданного на PHP, создается сессия. При этом, если у вас в cookie нет параметра с идентификатором сессии (по-умолчанию это PHPSESSID), то сервер устанавливает вам куки и теперь сервер знает, какая сессия должна быть ассоциирована с вашим визитом.

В сессии хранятся данные, которые присущи только вашему визиту. Например, корзина товаров, информация о том, авторизовались ли вы для входа в личный кабинет и так далее.

Пока у вас есть в куки идентификатор сессии, сервер знает, кто вы и может показывать вам какие-либо ваши данные, хранимые в сессии.

По-умолчанию, в php есть встроенные функции session_start, session_write_close и прочие функции с префиксом session_. Полный набор функций можно посмотреть по ссылке:

https://www.php.net/manual/ru/ref.session.php

Также есть глобальный массив $_SESSION, в который вы можете записывать что угодно. MODX также записывает в него свои служебные данные.

Пока сессия открыта и пока не вызван метод session_write_close, все манипуляции с данными в $_SESSION будут сохраняться при переходах между страницами в рамках вашего посещения.

По-умолчанию, в php сессии хранятся в файлах, хранимых во временном каталоге (например /tmp). Также в дистрибутиве php содержится bash-скрипт sessionclean, который автоматически устанавливается в системные таймеры (можно сказать — это cron) и с периодичностью раз в 30 минут очищает устаревшие сессии, чтобы они не занимали лишнее пространство на диске.

Сессии устаревают по принципу "Если с момента последнего обращения к сессии прошло больше времени, чем укзано в настройке php session.gc_maxlifetime, то считаем ее устаревшей".

Для чего нужны сессии

Сессии — это временные хранилища, связанные с конкретным посетителем, а точнее с браузером. В сессии хранятся данные, потеря которых не несет существенных последствий.

Как и где инициализируются обработчики сессии в MODX

Прежде, чем сказать о том, как это происходит в MODX, важно сказать, что php предусматривает переопределение стандартных механизмов работы с сессиями. Это делается при помощи встроенной в php функции session_set_save_handler, с помощью которой мы можем переопределить способ чтения, записи и очистки от устаревших сессий (сборки мусора).

В MODX это происходит в методе modX::_initSession.

Стандартный механизм обработки сессий в MODX реализован в классе modSessionHandler. И, как вы наверняка догадались, сессии MODX хранятся в базе данных, а точнее в таблице modx_session. Там хранятся ID сессии (это как раз то, что хранится в куки в переменной PHPSESSID), время последнего доступа к ним и, собственно, сериализованный массив $_SESSION.

Сборщик мусора и настройки gc_probability и gc_divisor

В php механизм очистки от устаревших сессий запускается в соответствии с настройками php.ini session.gc_probability и session.gc_divisor. Очистка происходит session.gc_probability раз из session.gc_divisor.

Например, если session.gc_probability = 1, а session.gc_divisor = 100, то очистка будет происходить в одном случае из ста обращений к сайту, для того, чтобы не перегружать сервер слишком частыми процедурами очистки сессий.

Если ваш сайт посещают часто, то нужно увеличивать параметр session.gc_divisor. Например, ваш сайт посещают 1000 раз в сутки (речь идет именно об обращении к страницам, которые может делать хоть один и тот же пользователь), тогда при соотношении session.gc_probability/session.gc_divisor 1/1000 хотя бы один раз в сутки у вас будет запускаться механизм очистки от устаревших сессий.

Garbage collector

В переводе с английского означает "сборщик мусора". Сокращенно gc. Универсальное понятие, используемое в разных языках программирования, означающее механизм очистки от устаревших данных.

Как вы понимаете, механизм очистки от устаревших сессий можно запустить и самостоятельно (выше мы говорили о скрипте sessionclean). В Ubuntu этот механизм запускается каждые 30 минут. За другие ОС не скажу.

В MODX процедура очистки от устаревших сессий происходит в методе modSessionHandler::gc. И тут стоит отметить, что механизм очистки сессии в скрипте sessionclean, запускаемый каждые 30 минут, не будет очищать сессии MODX, так как он ничего не знает о том, что MODX переопределяет стандартные механизмы в классе modSessionHandler. Поэтому, если вы хотите очищать сессии modx вручную, необходимо написать самостоятельно скрипт. Но смысла в этом большого нет, кроме случая, описанного в конце этой статьи, так как очистка сессий будет происходить согласно вашим настройка session.gc_probability и session.gc_divisor.

Время жизни куки и сессий

В настройках php, в php.ini есть настройка session.gc_maxlifetime, которая определяет время с момента последнего обращения к сессии, после которого сессия считается устаревшей и будет удалена во время следующей очистки сессий (сборке мусора) и session.cookie_lifetime, которая определяет время жизни куки PHPSESSID в вашем браузере.

Стоит отметить, что MODX переопределяет эти настройки, согласно своим аналогичным системным настройкам session_cookie_lifetime и session_gc_maxlifetime, поэтому в первую очередь смотрите именно в системные настройки вашего сайта на MODX.

Логично, что выставлять cookie_lifetime больше, чем gc_maxlifetime смысла нет.

Почему моя таблица modx_session весит 2Гб?

Впервые с вопросом, как работают сессии в MODX я столкнулся, когда начал замечать, что если я давно не занимался каким-либо сайтом, то на сервер, где он расположен, таблица modx_session становится непомерно огромной, что порой это приводит к замедлению работы сайта из-за долгой выборки сессии из этой таблицы.

Стоит понимать, что сессия создается независимо от того, является ли пользователь авторизованным или нет. Как только на ваш сайт попадает новый пользователь, у вас автоматически появляется новая запись в таблице modx_session. А если он это делает еще и с нескольких устройств, то количество записей будет равно количеству устройств. Правда, это можно исправить, выключив системную настройку anonymous_sessions. Но, если у вас интернет-магазин, то выключать ее нельзя, иначе у неавторизованных пользователей не будет работать, например, корзина.

Учитывая все вышеописанное, такое происходит только потому, что данная таблица не очищается от устаревших сессий.

Как это можно решить?

  1. Проверить параметр session.gc_probability на сервере, он должен быть больше 0. Если он равен 0, значит механизм очистки от старых сессий не запускается никогда.
  2. Проверить, насколько часто запускается механизм очистки сессий. Если, к примеру, параметр session.gc_divisor очень большой, то возможно, при вашей посещаемости, очистка от устаревших сессий происходит 1 раз в год или еще реже.
  3. Проверить время жизни сессий (настройка MODX session_gc_maxlifetime). Если она очень большая, например 1 год, то, очевидно, в течение года сессия будет оставаться живой. А если, при этом, параметр session_cookie_lifetime значительно меньше этого значения, то на одного пользователя потенциально будет создаваться больше, чем одна сессия, что является излишним.
  4. Если возможности изменить настройку сессий session.gc_probability или session.gc_divisor у вас нет, то есть смысл написать cron-скрипт, о котором я писал выше.

Доходило до такого, что таблица modx_session ввиду отсутствия корректных настроек сессии и обслуживания сайта, разрасталась до нескольких гигабайт

Как можно улучшить стандартный механизм работы с сессиями в MODX

Ко мне пришла в голову идея расширить стандартный класс modSessionHandler, заставив его работать с другой таблицей, которая представляет собой копию modx_session, но где мы можем добавить свои столбцы, например столбец anonymous, столбец user_id и прочие полезные столбцы, по которым мы могли бы делать выборку сессий.

После таких манипуляций, необходимо указать системную настройку session_handler_class, вставив туда название нашего кастомного класса для работы с сессиями (при этом, к сожалению, нет настройки для указания пути к классу, поэтому файл придется класть в системный каталог MODX, туда же, где лежит файл modsessionhandler.class.php).

Какие это может дать преимущества

  1. При очистке сессий мы можем делать разные условия для времени жизни сессии авторизованных пользователей и анонимных. Например, сессиий анонимных пользователей действительны в течение одних суток, а авторизованных — в течение 7 дней.
  2. При авторизации одного и того же пользователя с разных устройств, мы могли бы удалять его другую сессию, предотвращая тем самым одновременный вход с разных устройств (если такое предусмотрено логикой вашего сайта).
  3. При авторизации одного и того же пользователя с разных IP мы могли бы сообщать о подозрительной активности.
  4. Можно расширить эту таблицу прочими полезными столбцами, которые мы могли бы использовать себе во благо и расширять сайт дополнительным функционалом.

Заключение

Спасибо за внимание! Надеюсь, статья была интересной и вдохновит кого-то из читателей на новые идеи.

Не забывайте подписываться на мой youtube-канал: OpenModx!

Мы используем куки на нашем сайте. Продолжая просмотр, вы соглашаетесь с условями пользовательского соглашения Я согласен
Пожалуйста, подождите. Процесс оформления заказа может занимать до 30 секунд.