Корректное завершение работы
OxPHP обрабатывает SIGTERM и SIGINT, чтобы выполняющиеся запросы успевали завершиться до выхода из процесса. Это важно для развёртываний без простоя и плавающих обновлений (rolling updates) в системах оркестрации контейнеров.
Обработка сигналов
OxPHP реагирует на два сигнала завершения:
| Сигнал | Источник | Поведение |
|---|---|---|
SIGTERM |
Оркестраторы контейнеров, docker stop, kill |
Запускает корректное завершение работы |
SIGINT |
Ctrl+C в терминале | Запускает корректное завершение работы |
Оба сигнала запускают одну и ту же последовательность завершения. Достаточно только первого сигнала. Сервер немедленно начинает слив соединений.
Последовательность завершения
При получении сигнала завершения OxPHP выполняет следующую последовательность:
- Прекращение приёма новых соединений — сервер перестаёт принимать новые TCP-соединения на основном порту. PHP-воркеры продолжают работать, обрабатывая выполняющиеся запросы.
- Сворачивание активных соединений — HTTP/2-клиенты получают кадр
GOAWAY, а простаивающие keep-alive-соединения HTTP/1.1 закрываются, чтобы клиенты переходили на исправный экземпляр, а не мультиплексировали новые запросы в завершающийся. Открытые потоки завершаются быстро и аккуратно — сюда относится любой ответ, который уже начал сбрасывать вывод по частям (chunked), — как конечные загрузки, так и SSE — см. Server-Sent Events. - Слив выполняющихся запросов — обычные активные запросы не прерываются и завершаются с полными ответами. Сервер проверяет их завершение каждые 100 мс. Внутренний сервер проверок работоспособности и метрик остаётся доступным на протяжении всего слива, поэтому проверки готовности (readiness probes) продолжают работать.
- Соблюдение крайнего срока слива — запросы, всё ещё выполняющиеся по истечении
DRAIN_TIMEOUT_SECONDS, отменяются (их коллбэкиregister_shutdown_function()при этом всё равно выполняются) и получают ~2 секунды на завершение, прежде чем сервер продолжит. - Сброс плагинов — записи access-log и APM-спаны, накопленные в буфере во время окна слива, сбрасываются.
- Остановка асинхронного пула — фоновый пул асинхронных задач останавливается.
- Остановка внутреннего сервера — сервер проверок работоспособности и метрик останавливается после завершения слива.
- Выход — процесс завершается с кодом состояния 0.
PHP-воркеры останавливаются явно на шаге 1 — метод shutdown() исполнителя вызывается в рамках остановки основного сервера, что даёт потокам воркеров сигнал завершиться после обработки любого текущего запроса.
Конфигурация
| Переменная | По умолчанию | Описание |
|---|---|---|
DRAIN_TIMEOUT_SECONDS |
25 |
Максимальное число секунд, которое выполняющиеся запросы получают на завершение, прежде чем будут отменены; процесс завершается в течение ~2 секунд после крайнего срока. Значение по умолчанию оставляет запас для завершения после крайнего срока и сброса телеметрии в рамках стандартного 30-секундного периода отсрочки завершения (termination grace period) в Kubernetes |
Задавайте DRAIN_TIMEOUT_SECONDS с учётом самого медленного ожидаемого запроса:
- API-серверы с быстрыми ответами:
10–15секунд - Приложения с загрузкой файлов или долгими запросами:
30–60секунд - Режим воркеров с фоновой обработкой: под самую длинную ожидаемую операцию
Kubernetes
В Kubernetes процесс завершения при плавающем обновлении (rolling update) выглядит так:
- Kubernetes отправляет
SIGTERMв под. - Под удаляется из списка эндпоинтов Service.
- OxPHP сливает выполняющиеся соединения в пределах
DRAIN_TIMEOUT_SECONDS, затем отменяет отставших и завершается ещё в течение ~2 секунд. - Если под всё ещё работает по истечении
terminationGracePeriodSeconds, Kubernetes отправляетSIGKILL.
Задавайте terminationGracePeriodSeconds больше, чем DRAIN_TIMEOUT_SECONDS + 2, чтобы слив — включая завершение после крайнего срока и сброс телеметрии — успел завершиться до принудительного завершения:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
terminationGracePeriodSeconds: 45
containers:
- name: oxphp
image: ghcr.io/oxphp/oxphp:0.10.0
env:
- name: DRAIN_TIMEOUT_SECONDS
value: "30"Хук pre-stop
Если ваш сервис получает трафик от внешних балансировщиков нагрузки, которые медленно распространяют изменения эндпоинтов, добавьте хук pre-stop, чтобы отложить последовательность завершения:
lifecycle:
preStop:
exec:
command: ["sleep", "5"]Это даёт балансировщику нагрузки время убрать под из своего списка целей, прежде чем OxPHP прекратит принимать соединения.
Docker
Docker отправляет SIGTERM при выполнении docker stop. Стандартный таймаут остановки Docker составляет 10 секунд, после чего Docker отправляет SIGKILL.
Чтобы дать OxPHP достаточно времени на слив, увеличьте таймаут остановки:
docker stop --time 45 my-oxphp-containerИли задайте его в вашем файле Compose:
services:
oxphp:
image: ghcr.io/oxphp/oxphp:0.10.0
stop_grace_period: 45s
environment:
DRAIN_TIMEOUT_SECONDS: "30"Сообщения в логах
Во время корректного завершения работы OxPHP выводит структурированные сообщения в логах, которые можно отслеживать:
Успешный слив:
{"level":"INFO","message":"Received shutdown signal, draining connections"}
{"level":"INFO","message":"Draining in-flight connections","active_connections":3}
{"level":"INFO","message":"All connections drained"}
{"level":"INFO","message":"Server stopped"}Достигнут крайний срок слива:
{"level":"INFO","message":"Received shutdown signal, draining connections"}
{"level":"WARN","message":"Drain timeout reached, cancelling in-flight requests","remaining_connections":1}
{"level":"INFO","message":"All connections drained"}
{"level":"INFO","message":"Server stopped"}Если вы регулярно видите предупреждение «Drain timeout reached», увеличьте DRAIN_TIMEOUT_SECONDS или исследуйте долго выполняющиеся запросы с помощью гистограммы oxphp_request_duration_us.
См. также
- Проверки работоспособности — взаимодействие проверок готовности (readiness probes) и завершения работы
- Справочник по конфигурации — все переменные окружения, включая
DRAIN_TIMEOUT_SECONDS - Метрики —
oxphp_active_connectionsотслеживает соединения во время слива