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

в 7:53, , рубрики: Apache, php, php-fpm, web-разработка, Блог компании 1С-Битрикс, Веб-разработка, системное администрирование, метки: , , ,

Всем привет!

В последнее время, в связи с бурным ростом и усложнением фронт-эндов, аяксами и т.п. — все чаще проявляется проблема блокировки сессий во время эксплуатации сайтов на 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 содержат большой набор полезных инструментов, но, к сожалению, далеко не все ими умеют пользоваться. Надеюсь подобные практические статьи будут полезны не только системным администраторам, но и веб-разработчикам.

Автор: AlexSerbul

Источник


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js