Как я начал ковыряться в этом вопросе
Меня зовут Артем Зернов. Я веб-разработчик, занимаюсь развитием проекта Лектория, а также веду свой 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 хотя бы один раз в сутки у вас будет запускаться механизм очистки от устаревших сессий.
В переводе с английского означает "сборщик мусора". Сокращенно 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. Но, если у вас интернет-магазин, то выключать ее нельзя, иначе у неавторизованных пользователей не будет работать, например, корзина.
Учитывая все вышеописанное, такое происходит только потому, что данная таблица не очищается от устаревших сессий.
Как это можно решить?
- Проверить параметр session.gc_probability на сервере, он должен быть больше 0. Если он равен 0, значит механизм очистки от старых сессий не запускается никогда.
- Проверить, насколько часто запускается механизм очистки сессий. Если, к примеру, параметр session.gc_divisor очень большой, то возможно, при вашей посещаемости, очистка от устаревших сессий происходит 1 раз в год или еще реже.
- Проверить время жизни сессий (настройка MODX session_gc_maxlifetime). Если она очень большая, например 1 год, то, очевидно, в течение года сессия будет оставаться живой. А если, при этом, параметр session_cookie_lifetime значительно меньше этого значения, то на одного пользователя потенциально будет создаваться больше, чем одна сессия, что является излишним.
- Если возможности изменить настройку сессий session.gc_probability или session.gc_divisor у вас нет, то есть смысл написать cron-скрипт, о котором я писал выше.
Доходило до такого, что таблица modx_session ввиду отсутствия корректных настроек сессии и обслуживания сайта, разрасталась до нескольких гигабайт
Как можно улучшить стандартный механизм работы с сессиями в MODX
Ко мне пришла в голову идея расширить стандартный класс modSessionHandler, заставив его работать с другой таблицей, которая представляет собой копию modx_session, но где мы можем добавить свои столбцы, например столбец anonymous, столбец user_id и прочие полезные столбцы, по которым мы могли бы делать выборку сессий.
После таких манипуляций, необходимо указать системную настройку session_handler_class, вставив туда название нашего кастомного класса для работы с сессиями (при этом, к сожалению, нет настройки для указания пути к классу, поэтому файл придется класть в системный каталог MODX, туда же, где лежит файл modsessionhandler.class.php).
Какие это может дать преимущества
- При очистке сессий мы можем делать разные условия для времени жизни сессии авторизованных пользователей и анонимных. Например, сессиий анонимных пользователей действительны в течение одних суток, а авторизованных — в течение 7 дней.
- При авторизации одного и того же пользователя с разных устройств, мы могли бы удалять его другую сессию, предотвращая тем самым одновременный вход с разных устройств (если такое предусмотрено логикой вашего сайта).
- При авторизации одного и того же пользователя с разных IP мы могли бы сообщать о подозрительной активности.
- Можно расширить эту таблицу прочими полезными столбцами, которые мы могли бы использовать себе во благо и расширять сайт дополнительным функционалом.
Заключение
Спасибо за внимание! Надеюсь, статья была интересной и вдохновит кого-то из читателей на новые идеи.
Не забывайте подписываться на мой youtube-канал: OpenModx!
Вы должны авторизоваться, чтобы оставлять комментарии.