Подтвердить что ты не робот

Случайная потеря данных сеанса в PHP

Вот проблема, с которой мы столкнулись в течение последних нескольких недель.

1/Наша настройка

  • PHP 5.4 + MySQL
  • 2 выделенных сервера с балансировкой нагрузки
  • Сессии реплицируются между двумя серверами с помощью memcached
  • 3 приложения, работающие на этих серверах:
    • Одно настраиваемое приложение, использующее настройки сеанса php по умолчанию.
    • Другое настраиваемое приложение, использующее разные настройки сеанса (имя файла cookie, путь)
    • Один Wordpress CMS

2/Проблема

Проблема возникает в нашем первом приложении.

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

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

  • Некоторые из них включают представление формы (переменная $_SESSION изменена)
  • Другие просто включают открытие всплывающей страницы без изменения данных сеанса.

Мы попытались воспроизвести различные сценарии, описанные пользователями: иногда мы были в состоянии, но большую часть времени у нас нет проблем, что затрудняет отладку.

Другие примечания:

  • Проблема в последнее время, это приложение работало годами без каких-либо проблем.
  • Это не похоже на нашу загрузку на сервере, потому что проблема все еще возникала во время летнего перерыва, когда наш трафик был низким.
  • Это влияет только на один сеанс/пользователей за раз: все остальные пользователи, входящие в систему одновременно, не испытывают этой проблемы.
  • Проблема возникла во всех браузерах (IE, Firefox, Chrome).

3/Технический анализ

Когда происходит отключение, пользователь перенаправляется на страницу "Ваш сеанс истек или у вас нет права на просмотр". Когда эта страница загружена, мы получаем техническое письмо с дампом переменной $_SESSION.

Когда сеанс заканчивается обычным способом, полученное письмо показывает, что переменная $_SESSION пуста (нормальное поведение). Когда происходит неожиданное отключение, интересно, что $_SESSION не является полностью пустым: из 20 элементов, содержащихся в массиве, остается только один (всегда один и тот же).

Таким образом, это означает, что сеанс не истек, но недостаточно данных для "идентификации" пользователя, поэтому отображается страница "без прав". В качестве подтверждения, когда это происходит, мы можем проверить memcached, что этот сеанс по-прежнему содержит некоторые данные.

Это потенциальная проблема, которую мы выявили до сих пор, и что мы сделали, чтобы исключить их:

  • Memcached указывает между 70 и 80% freespace, поэтому мы не думаем, что это проблема.
  • Мы удалили Memcached и вернулись к использованию общего каталога NFS для файлов сеанса: проблема действительно ухудшилась. Это указывает на аппликативную ошибку, поскольку NFS медленнее записывает данные, потеря сеанса будет происходить чаще.
  • Мы просмотрели все различные форумы (включая SO), рассказывая о потере данных сеанса PHP, и рассмотрели наш код соответственно. База кода большая, но мы использовали автоматические инструменты и скрипты, чтобы не пропускать файл.
    • session_start() вызывается в начале каждой страницы.
    • exit() вызывается после каждого заголовка ( "Местоположение..." )
    • register_globals отключен
  • Мы проверили возможные перерывы между нашими двумя другими приложениями и проблемными, хотя они не разделяют никакой обработки кода, базы данных или сеанса. Там ничего не было.
  • Мы проанализировали наши журналы доступа во время разрывов, чтобы проверить шаблоны поведения: вам тоже не повезло.

Итак, мы понятия не имеем, что вызывает эту проблему, поскольку это происходит случайно, поэтому мои вопросы:

  • Проблема может исходить из нашего кода: мы пропустили что-нибудь, чтобы проверить? Это решение кажется маловероятным, поскольку код работает в большинстве случаев для всех наших пользователей, но я все еще рассматриваю его.
  • Проблема может возникнуть из другого приложения/процесса, который будет "пустым" частью массива переменных сеанса. Мы также рассмотрели код из других приложений, но не нашли ничего, что могло бы вызвать это. И если другой процесс делает это, почему он должен только пустить несколько сеансов, а не все из них?

Спасибо за вашу помощь.

4b9b3361

Ответ 1

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

Тем не менее, я предполагаю, что вы memcached.sess_locking отключены или если у вас есть пользовательская реализация сеанса - что она не вообще блокировать блокировку.
В конечном итоге это приводит к условию гонки между двумя одновременными HTTP-запросами.

Мое предположение основано на часто встречающемся плохом совете, чтобы как можно скорее отключить блокировки или освободить их, чтобы достичь более высокой производительности.

Ответ 2

Если эта проблема возникла "внезапно", проверьте, что изменилось. Выполняли ли вы какую-либо работу над приложением? Если это так, проверьте код (вы говорили об автоматизированных инструментах, поэтому я ожидаю, что там будет репозиторий, который позволит точно находить изменения кода). Вы что-то изменили на сервере? Как обновить программное обеспечение, обновить/изменить оборудование, внести изменения в другие два приложения? Одна вещь, которая появилась на ум, вы проверили диски, которые вы используете для кеширования? Это может быть поврежденная часть файловой системы. Это объясняет случайную часть пользователя.

Я пару вещей, которыми я всегда принадлежу:

  • Попробуйте определить момент первого появления как можно более точный. В моей работе это иногда вызывает то, что кто-то говорит: "О да, может быть, это связано с тем, когда я изменил/обновил/создал то или это", чтобы это могло помочь. С другой стороны, иногда это может занять несколько дней, недель или более, прежде чем что-то заметят, поэтому начните расширять этот временной интервал, если ничего не появится.
  • У вас уже есть пара сценариев, найти общий фактор. Если они не разделяют какой-либо код, прекратите смотреть туда. Если они ДОЛЖНЫ разделять поиск кода там. Конечно, совместное использование (часть) здесь может помочь нам в поиске.
  • Сделайте организованный поиск. Обычно я выполняю основную проверку приложения, когда я работаю больше всего над приложением (или даже лучше, когда создаю его). Коллега проверяет окружающие приложения, которые могут повлиять на него. В вашем случае эти 2 других приложения. Наконец, наш sysadmin проверит наличие недавно установленного или обновленного программного обеспечения на сервере (серверах), и он также проверит с нашими сетевыми ребятами, если что-то изменит аппаратное или сетевое взаимодействие (для других это может быть хостинг-провайдер).

Ответ 3

Он может быть таким же простым, как плагин WordPress, который использует сеансы и вызывает либо session_name(), либо session_id() с другим значением, перекрывая ваши пользовательские приложения с настройками сеанса по умолчанию.

Так как WordPress сам не использует сеансы, плагины часто пишутся с точки зрения свободной воли с сеансами. Я просто выполнил поиск на тестовом сайте WordPress и нашел сеансы, используемые в плагине галереи, плагин для размещения фонового изображения на странице, плагин корзины покупок и плагин, который я писал, для переноса загруженного файла с одного admin страница к другому.