⚡ fastcgi_finish_request(): Не заставляй клиентов ждать!

Cитуация: клиент отправил запрос к вашему api, ждёт ответ, а тебе нужно сделать что-то тяжёлое. Отправить письмо, обработать видео, записать логи в базу.

Клиент сидит и ждёт. Секунды идут. Потом ещё идут.

А можно сделать так, чтобы клиент получил ответ мгновенно, а тяжёлая работа продолжилась в фоне. Без очередей, без кронов, без лишних телодвижений.

Тут нам поможет функция PHP fastcgi_finish_request()


🎯 Что это за функция

fastcgi_finish_request() - закрывает соединение с клиентом, отдаёт ему все накопленные данные, но при этом PHP-процесс продолжает работать.

Функция появилась в PHP 5.3.3. Работает только со связкой PHP-FPM. Ещё есть LiteSpeed, но я с ним не работал. Если у тебя Apache с mod_php - не сработает.


📦 Как это выглядит в коде

<?php

// Отдаём клиенту всё, что накопили
echo json_encode(['status' => 'ok']);

// Закрываем соединение
fastcgi_finish_request();

// Дальше всё, что выполняется - уже без клиента
sleep(10); // клиент об этом не узнает
file_put_contents('/tmp/log.txt', 'Тяжёлая работа завершена');

Клиент получил ответ мгновенно, а сервер спокойно доделывает свои дела.


⚠️ Но есть нюансы (куда же без них)

1. Процесс не освобождается сразу

После вызова функции PHP-процесс продолжает висеть. Если у тебя много таких запросов - быстро упрёшься в pm.max_children и получишь 502-ю ошибку.

Как лечить: не используй для реально долгих задач. Отправить письмо - ок. Конвертировать 5 минут видео - нет.

2. Сессии блокируются

Если у тебя открыта сессия, следующий запрос от этого же пользователя будет ждать, пока сессия не закроется. Даже после вызова fastcgi_finish_request().

Ловил эту проблему, когда зависали запросы клиентов во время работы с админ-панелью. Переоткрытие вкладки в браузере не помогало. Каждый следующий запрос и новая вкладка просто висели в ожидании.

Причина оказалась простой: сессия была заблокирована фоновым процессом, который продолжал выполняться после fastcgi_finish_request().

Как лечить: вызывай session_write_close() до fastcgi_finish_request().

session_write_close();
fastcgi_finish_request();
// теперь сессия не блокирует следующие запросы

3. Таймауты всё ещё работают

max_execution_time продолжает тикать. Если твой фоновый код выполняется дольше - будет ошибка.

Как лечить: Эти ограничения не стоит убирать, timeout не просто так выставляются. Ошибки c timeout нужно фиксировать и устранять причину длительного выполнения процесса.


🎯 Итог

fastcgi_finish_request() - удобная штука для небольших фоновых задач, когда не хочется поднимать очереди. Но с нагрузкой нужно быть осторожным.


👇 А ты используешь эту функцию? Или предпочитаешь очереди?


Обсудить пост:

🔥 И не забудь подписаться :)