Локировки сессий в веб-проектах — выбираем эффективное оружие

от автора

Всем привет!

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

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

К сожалению, разработчики/сисадмины не всегда могут сразу понять, что дело в локировке сессии — и ищут проблемы в других частях проекта, теряя время.

В статье расскажу какие инструменты позволяют быстро диагностировать проблему, приведу работающий код и дам несколько боевых рекомендаций по выживанию 🙂


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

Диагностика

Рассмотрим, что происходит внутри операционной системы, если одновременно попытаться открыть в браузере (можно в разных вкладках) один засыпающий файл и несколько просто стартующих сессию скриптов:

<?php  session_start();  sleep(30);// только для одного скрипта ?> 

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

Процессы веб-сервера, в данном случае httpd, но то же самое происходит и с php-fpm — пытаются эксклюзивно залочить файл сессии, что видим с помощью lsof:

 lsof -n | awk '/sess_/' httpd      7079    nobody   52uW     REG                8,1        2216               809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd     10406    nobody   52u      REG                8,1        2216               809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd     10477    nobody   52u      REG                8,1        2216               809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd     10552    nobody   52u      REG                8,1        2216               809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd     11550    nobody   52u      REG                8,1        2216               809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 httpd     11576    nobody   52u      REG                8,1        2216               809832 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 

Обращаем внимание на 4 колонку. Число — это номер дескриптора файла в процесе, а дальше — тип локировки. «uW» — веб-сервер заблокировал файл эксклюзивно для записи. Остальные — ждут и нервно курят в сторонке:-) Как только процесс 7079 закончит свою работу, локировку «uW» возьмет другой процесс. В это время, понятно, выстраивается очередь и веб-интерфейс заметно тормозит. Еще веселее если несколько процессов залочат сессию на единицы секунды — интерфейс вообще станет колом.

Посмотрим теперь с другой стороны, чем занимаются процессы:

 ps -e -o pid,comm,wchan=WIDE-WCHAN-COLUMN | grep httpd 7079 httpd           - 10406 httpd           flock_lock_file_wait 10477 httpd           flock_lock_file_wait 10552 httpd           flock_lock_file_wait 11550 httpd           flock_lock_file_wait 11576 httpd           flock_lock_file_wait 

Во второй колонке видим, что все, кроме одного, заняты в функции «flock_lock_file_wait». А чем?

 strace -p 10406 Process 10406 attached - interrupt to quit flock(52, LOCK_EX) 

Правильно, в системном вызове c запросом эксклюзивной локировки.

 LOCK_EX  Place an exclusive lock.  Only one process may hold an                     exclusive lock for a given file at a given time. 

Полезный скрипт

Чтобы постоянно отслеживать на веб-серверах появление такого «паровозика», забивающего PHP-воркеры, я написал простой скриптик на AWK:

/sess_/ {     load_sessions[$9]++;     if (load_sessions[$9]>max_sess_link_count){         max_sess_link_count = load_sessions[$9];         max_sess_link_name = $9;     };      if ($4 ~ /.*uW$/ ){ locked_id[$9]=$2 }; }  END {      print max_sess_link_count, max_sess_link_name,locked_id[max_sess_link_name];      if (locked_id[max_sess_link_name] && max_sess_link_count>3) {         #    r=system("kill "locked_id[max_sess_link_name]);         #    if (!r) print "Locking process "locked_id[max_sess_link_name]" killed"         system("ls -al "max_sess_link_name);     }  } 

Запускается так:

 lsof -n | awk -f sess_view.awk 5 /tmp/sess_f629a13b4b0920a21042c86d17f4a6a6 24830 

Отображает длину «паровозика» и процесс — создающий затор.

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

Если же очень лень ( неужели я один такой 🙂 ), можно раскомментировать «kill» и отстреливать процессы веб-сервера, создающие коллапс и наслаждаться реакцией клиентов и менеджеров технической поддержки 🙂 Но правильнее конечно, купить 2-3 баночки пива и сходить в гости к разработчикам — с собранной подобным образом через cron в файлик статистикой и договориться о рефакторинге 🙂

Всем удачи и успехов!

P.S.

Поддерживая большие проекты нашей компании, нам приходится постоянно создавать инструменты и методики для быстрого анализа проблем с производительностью и их решения. GNU/Linux содержат большой набор полезных инструментов, но, к сожалению, далеко не все ими умеют пользоваться. Надеюсь подобные практические статьи будут полезны не только системным администраторам, но и веб-разработчикам.

ссылка на оригинал статьи http://habrahabr.ru/company/bitrix/blog/179803/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *