Рубріки: Highload

Управление сигналами pcntl в PHP

admin

Что произойдет, если работающий скрипт остановить? В случае сколь-нибудь сложной логики, последствия могут быть самыми плачевными:

  • Только часть данных запишется/обновится после выгрузки данных из файлов/баз/API.
  • Только часть пользователей получит рассылку либо же некоторые получат по два письма после перезапуска.
  • Только часть индекса будет перестроена.
  • и т.п.

Например, скрипт отправки ежедневной рассылки:

<?
foreach ( $users as $user )
{
  send($user['email'], $subject, $html);
  # <- тут происходит обрыв 
}

# только какая-то часть пользователей получит письмо

Понятно, что если прервать этот скрипт в середине цикла, часть пользователей останутся без писем.

PCNTL в PHP

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

Расширение pcntl позволяет получать и обрабатывать сигналы от операционной системы в PHP скриптах.

Простой скрипт, который перехватывает сигнал окончания работы SIGTERM:

<?


# назначаем обработчик сигнала
declare(ticks = 1);
pcntl_signal(SIGTERM, "sig_handler");


# обработчик сигнала
function sig_handler($signo)
{
        echo "\n" . 'received signal ' . $signo . "\n";
}


# бесконечный цикл
while ( true )
{
    for ( $i = 0; $i < 3; $i++ )
    {
        echo '.';
        sleep(1);
    }

    echo "\n";
}

# Пример перехвата сигнала, посылаемого командой kill

  • Инструкция declare(ticks = 1) нужна для инициализации обработки сигналов. Используйте ее в начале каждого скрипта, в котором нужен обработчик.
  • pcntl_signal назначает обработчик определенному сигналу.
  • В функции sig_handler() мы перехватываем сигнал и обрабатываем его. В нашем примере – просто выводим текст, вместо завершения скрипта.

Если запустить этот скрипт (php test.php), а в соседнем терминале попытаться его прервать командой pkill -f test.php, увидим такой вывод:

den@den:~# php test.php 
...
received signal 15
...
...
.
received signal 15
...

Обработка остановки работы

Для обработки остановки скрипта существуют такие сигналы:

  • SIGINT – прерывание процесса. Случается, когда пользователь оканчивает выполнение скрипта командой “ctrl+c”.
  • SIGTERM – окончание процесса. Происходит, когда процесс останавливают командой kill (либо другой командой, посылающей такой сигнал).

В хорошем скрипте нам нужно:

1. Обработать оба этих сигнала.

2. Иметь процедуру (набор инструкций), которые обязательно нужно выполнить перед завершением.

3. Только после окончания процедуры завершения остановить выполнение скрипта.

Для этого определим обработчики и процедуру завершения:

<?
declare(ticks = 1);


# обработаем сигналы завершения процесса
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");


# обработчик сигнала с процедурой завершения
function sig_handler($signo)
{
        # закончим выполнение задач
        echo "\n" . 'received quit signal, finishing tasks ' . "\n";
        for ( $i = 0; $i < 10; $i++ ) echo '-';
        echo "\n" . 'Done' . "\n";
        
        # остановим выполнение скрипта
        exit;
}


# бесконечный цикл
while ( true )
{
    for ( $i = 0; $i < 3; $i++ )
    {
        echo '.';
        sleep(1);
    }

    echo "\n";
}

# обработка любых сигналов об окончании работы

Теперь, если запустить скрипт и попробовать остановить его с помощью ctrl+c, увидим следующее:

den@den:~# php test.php 
..^C
received quit signal, finishing tasks 
----------
Done

# процедура завершения всегда будет выполнена перед остановкой скрипта

Обработка перезапуска

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

<?
declare(ticks = 1);


# объявляем настройки
$date = date('Y-m-d H:i:s');


# обработаем сигналы перезапуска процесса
pcntl_signal(SIGHUP, "sig_handler");



# обработчик сигнала с процедурой завершения
function sig_handler($signo)
{
        global $date;

        # обновляем настройки (дату)
        echo "\n" . 'Reloading config...' . "\n";
        $date = date('Y-m-d H:i:s');
}



# бесконечный цикл
while ( true )
{
    echo $date . ': ';

    for ( $i = 0; $i < 3; $i++ )
    {
        echo '.';
        sleep(1);
    }

    echo "\n";
}

# обработка сигнала о перезапуске процесса

Если запустить скрипт и в соседнем терминале вызвать команду:

pkill -HUP -f test.php

Теперь вернемся к работающему скрипту и увидим следующее:

den@den:~# php test.php 
2018-03-31 11:20:19: ...
2018-03-31 11:20:19: ...
Reloading config...

2018-03-31 11:20:24: ...
2018-03-31 11:20:24: ...

# скрипт был перезапущен

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

TL;DR

Для фоновых скриптов удобно использовать обработчики сигналов, чтобы обеспечить их правильную остановку и перезапуск. Сигналы SIGTERM и SIGNINT используются для прекращения работы скрипта, SIGHUP используется для перезапуска. В PHP обработка сигналов происходит с помощью функции pcntl_signal.

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023