Предыстория: с некоторых пор на сайте Х стали накапливаться процессы apache в состоянии отправки ответа, в логах никакого криминала не было обнаружено. Доблестные админы пытались спасти ситуацию тюнингом конфигов apache\nginx\mysql и всего что было связано с сайтом Х. Никаких результатов это не дало. Был реализован план Б, в забиксе был добавлен скрипт рестарта apache при накоплении более 100 процессов apache.
Такая ситуация меня очень заинтересовала, так как все это время я верил в миф про настройки всевозможных таймаутов, и думал что проблема плевая и решить её возможно с помощью правильно приготовленного конфига. Небольшие исследование привели к факту полного отсутствия возможностей как в PHP так и в Apache ограничения времени обработки самого запроса, да у Apache есть таймауты на операции ввода\вывода но они срабатывают до и после самой обработки запроса. Пресловутая настройка max_execution_time весьма синтетическая, так как контроль этого параметра происходит при обработки оп кодов PHP, как и вариант с register_tick_function. Тоесть если в PHP скрипте есть зависшая операция ввода\вывода (mysql сервер во время обработки запроса сбойнул и не прислал ответа, да бывает и такое), команда sleep, и прочие неизвестные полтергейст которых полно в больших проектах писаные несколькими поколениями разработчиков, то процесс apache будет висеть в памяти бесконечно долго и не будет обрабатывать входящие запросы.
По результатам исследования, вырисовывалось два варианта, первый: перевести проект на php-fpm у которого есть возможность контролировать время исполнения PHP скрипта, и второй: написать свой модуль для php только под линукс системы, который будет использовать стандартную возможность завести таймер и выполнится посредством сигнала прервав тем самым работу скрипта, сделать запись в лог stacktrace, отдать адекватный ответ и завершить текущий процесс ( так как PHP интерпретатор совершено не готов к такому развитию событий, либо не удалось его правильно приготовить ). Причина почему при реализации этого варианта приходится завершать процесс — при использовании PHP функции exit внутри сигнального обработчика приводит к падению процесса.
Плюсы и минусы:
Первый вариант:
плюсы — решение из коробки не страшно пускать в продакшен
минусы — перестройка проекта ( переделка .htaccess'ов под nginx либо прикручивание к apache fastcgi модуля),
не возможность узнать где произошло зависание
Второй вариант:
плюсы — информация о точном месте в коде где происходит зависание, полная управляемость включением такого таймера,
никаких глобальных переделок в проекте, всего один include и один вызов функции в корневом index.php
минусы — модуль хоть и примитивный ( пару функций ), может себя крайне интересно и непредсказуемо повести в продакшене
модуль на github'e
Итог: на одном из бекенд серверов будет использован второй вариант.
Такая ситуация меня очень заинтересовала, так как все это время я верил в миф про настройки всевозможных таймаутов, и думал что проблема плевая и решить её возможно с помощью правильно приготовленного конфига. Небольшие исследование привели к факту полного отсутствия возможностей как в PHP так и в Apache ограничения времени обработки самого запроса, да у Apache есть таймауты на операции ввода\вывода но они срабатывают до и после самой обработки запроса. Пресловутая настройка max_execution_time весьма синтетическая, так как контроль этого параметра происходит при обработки оп кодов PHP, как и вариант с register_tick_function. Тоесть если в PHP скрипте есть зависшая операция ввода\вывода (mysql сервер во время обработки запроса сбойнул и не прислал ответа, да бывает и такое), команда sleep, и прочие неизвестные полтергейст которых полно в больших проектах писаные несколькими поколениями разработчиков, то процесс apache будет висеть в памяти бесконечно долго и не будет обрабатывать входящие запросы.
По результатам исследования, вырисовывалось два варианта, первый: перевести проект на php-fpm у которого есть возможность контролировать время исполнения PHP скрипта, и второй: написать свой модуль для php только под линукс системы, который будет использовать стандартную возможность завести таймер и выполнится посредством сигнала прервав тем самым работу скрипта, сделать запись в лог stacktrace, отдать адекватный ответ и завершить текущий процесс ( так как PHP интерпретатор совершено не готов к такому развитию событий, либо не удалось его правильно приготовить ). Причина почему при реализации этого варианта приходится завершать процесс — при использовании PHP функции exit внутри сигнального обработчика приводит к падению процесса.
Плюсы и минусы:
Первый вариант:
плюсы — решение из коробки не страшно пускать в продакшен
минусы — перестройка проекта ( переделка .htaccess'ов под nginx либо прикручивание к apache fastcgi модуля),
не возможность узнать где произошло зависание
Второй вариант:
плюсы — информация о точном месте в коде где происходит зависание, полная управляемость включением такого таймера,
никаких глобальных переделок в проекте, всего один include и один вызов функции в корневом index.php
минусы — модуль хоть и примитивный ( пару функций ), может себя крайне интересно и непредсказуемо повести в продакшене
модуль на github'e
Итог: на одном из бекенд серверов будет использован второй вариант.