Łagodne zamknięcie
OxPHP obsługuje SIGTERM i SIGINT tak, aby będące w trakcie realizacji żądania zdążyły się zakończyć, zanim proces się zamknie. Ma to znaczenie dla wdrożeń bez przestojów oraz aktualizacji kroczących (rolling updates) w orkiestracji kontenerów.
Obsługa sygnałów
OxPHP reaguje na dwa sygnały zamknięcia:
| Sygnał | Źródło | Zachowanie |
|---|---|---|
SIGTERM |
Orkiestratory kontenerów, docker stop, kill |
Rozpoczyna łagodne zamknięcie |
SIGINT |
Ctrl+C w terminalu | Rozpoczyna łagodne zamknięcie |
Oba sygnały uruchamiają tę samą sekwencję zamknięcia. Wystarczy tylko pierwszy sygnał. Serwer natychmiast rozpoczyna wygaszanie.
Sekwencja zamknięcia
Po otrzymaniu sygnału zamknięcia OxPHP wykonuje następującą sekwencję:
- Zaprzestanie przyjmowania nowych połączeń — serwer przestaje przyjmować nowe połączenia TCP na głównym porcie. Workery PHP nadal działają, przetwarzając żądania będące w trakcie realizacji.
- Wygaszanie aktywnych połączeń — klienci HTTP/2 otrzymują ramkę
GOAWAY, a bezczynne połączenia keep-alive HTTP/1.1 zostają zamknięte, dzięki czemu klienci przechodzą na sprawną instancję, zamiast multipleksować nowe żądania do zamierającej. Otwarte strumienie kończone są sprawnie i czysto — liczy się każda odpowiedź, która zaczęła wysyłać dane w trybie chunked, zarówno skończone pobrania, jak i SSE — zobacz Server-Sent Events. - Wygaszanie żądań w trakcie realizacji — zwykłe aktywne żądania są pozostawione, aby dokończyć się z pełnymi odpowiedziami. Serwer sprawdza ich zakończenie co 100 ms. Wewnętrzny serwer stanu/metryk pozostaje dostępny przez cały czas wygaszania, dzięki czemu sondy gotowości (readiness) nadal działają.
- Egzekwowanie terminu wygaszania — żądania nadal działające po upływie
DRAIN_TIMEOUT_SECONDSzostają anulowane (ich funkcje wywoływane zwrotnie przezregister_shutdown_function()i tak są uruchamiane) i otrzymują ~2 sekundy na uporządkowanie, zanim serwer przejdzie dalej. - Opróżnienie buforów wtyczek — wpisy rejestru dostępu oraz spany APM zbuforowane w oknie wygaszania zostają zapisane.
- Zamknięcie puli asynchronicznej — pula asynchronicznych zadań w tle zostaje zatrzymana.
- Zatrzymanie serwera wewnętrznego — serwer stanu/metryk zostaje zatrzymany po zakończeniu wygaszania.
- Wyjście — proces kończy działanie z kodem statusu 0.
Workery PHP są zatrzymywane jawnie w kroku 1 — metoda shutdown() egzekutora jest wywoływana w ramach zatrzymywania głównego serwera, co sygnalizuje wątkom worker, aby zakończyły działanie po dokończeniu ewentualnego trwającego żądania.
Konfiguracja
| Zmienna | Wartość domyślna | Opis |
|---|---|---|
DRAIN_TIMEOUT_SECONDS |
25 |
Maksymalna liczba sekund, jaką będące w trakcie realizacji żądania dostają na zakończenie, zanim zostaną anulowane; proces kończy działanie w ciągu ~2 sekund po upływie terminu. Wartość domyślna pozostawia zapas na porządkowanie po terminie i opróżnienie telemetrii w ramach domyślnego 30-sekundowego okresu karencji na zakończenie (termination grace period) w Kubernetes |
Ustaw DRAIN_TIMEOUT_SECONDS tak, aby uwzględniał najwolniejsze spodziewane żądanie:
- Serwery API z szybkimi odpowiedziami:
10–15sekund - Aplikacje z przesyłaniem plików lub długimi zapytaniami:
30–60sekund - Tryb worker z przetwarzaniem w tle: dopasuj do najdłuższej spodziewanej operacji
Kubernetes
W Kubernetes przebieg zamknięcia podczas aktualizacji kroczącej wygląda następująco:
- Kubernetes wysyła
SIGTERMdo poda. - Pod zostaje usunięty z listy endpointów Service.
- OxPHP wygasza połączenia będące w trakcie realizacji w czasie
DRAIN_TIMEOUT_SECONDS, następnie anuluje maruderów i kończy działanie w ciągu ~2 kolejnych sekund. - Jeśli pod nadal działa po upływie
terminationGracePeriodSeconds, Kubernetes wysyłaSIGKILL.
Ustaw terminationGracePeriodSeconds powyżej wartości DRAIN_TIMEOUT_SECONDS + 2, aby wygaszanie — łącznie z porządkowaniem po terminie i opróżnieniem telemetrii — zdążyło się zakończyć przed wymuszonym zabiciem procesu:
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"Hak pre-stop
Jeśli twoja usługa odbiera ruch z zewnętrznych load balancerów, które wolno propagują zmiany endpointów, dodaj hak pre-stop, aby opóźnić sekwencję zamknięcia:
lifecycle:
preStop:
exec:
command: ["sleep", "5"]Daje to load balancerowi czas na usunięcie poda ze swojej listy celów, zanim OxPHP przestanie przyjmować połączenia.
Docker
Docker wysyła SIGTERM, gdy uruchamiasz docker stop. Domyślny limit czasu zatrzymania w Docker wynosi 10 sekund, po którego upływie Docker wysyła SIGKILL.
Aby dać OxPHP wystarczająco dużo czasu na wygaszenie, zwiększ limit czasu zatrzymania:
docker stop --time 45 my-oxphp-containerAlbo ustaw go w pliku Compose:
services:
oxphp:
image: ghcr.io/oxphp/oxphp:0.10.0
stop_grace_period: 45s
environment:
DRAIN_TIMEOUT_SECONDS: "30"Komunikaty w logach
Podczas łagodnego zamknięcia OxPHP emituje ustrukturyzowane komunikaty w logach, które możesz monitorować:
Udane wygaszenie:
{"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"}Osiągnięto termin wygaszania:
{"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"}Jeśli regularnie widzisz ostrzeżenie „Drain timeout reached", zwiększ DRAIN_TIMEOUT_SECONDS lub zbadaj długo działające żądania za pomocą histogramu oxphp_request_duration_us.
Zobacz też
- Kontrole stanu — sondy gotowości i interakcja z zamknięciem
- Dokumentacja konfiguracji — wszystkie zmienne środowiskowe, w tym
DRAIN_TIMEOUT_SECONDS - Metryki —
oxphp_active_connectionsśledzi połączenia podczas wygaszania