2016-07-17 10 views
16

Pracuję nad aplikacją, która okresowo nazywa procesy w tle. Jeden z nich został wywołany przez crona, ale szukam czegoś solidniejszego, więc zamieniam go, by działał pod nadzorem. (Prawdopodobnie uruchomi się na 10 minut, w czasie których może wykryć pracę do wykonania lub bezczynnie. Po jej zakończeniu przełożony automatycznie odradzi czystą instancję.)Czy mogę polegać na funkcji register_shutdown_function() na SIGTERM, jeśli skonfigurowano pcntl_signal()?

Ponieważ inspektor lepiej jest zapewnić, że tylko podana liczba instancji coś działa równolegle, mogę uciec z uruchomieniem ich dłużej. Oznacza to jednak, że moje procesy są bardziej skłonne do odbierania sygnałów końcowych, albo bezpośrednio od kill, albo dlatego, że zostały zatrzymane przez Supervisora. Dlatego eksperymentuję z tym, jak sobie z tym poradzić w PHP.

Wygląda podstawowym rozwiązaniem jest użycie pcntl_signal() tak:

declare(ticks = 1); 
pcntl_signal(SIGTERM, 'signalHandler'); 
pcntl_signal(SIGINT, 'signalHandler'); 

function signalHandler($signal) { 
    switch($signal) { 
     case SIGTERM: 
     case SIGINT: 
      echo "Exiting now...\n"; 
      exit(); 
    } 
} 

Mam jednak kilka punktów w moim kodu, który może zrobić z ostrożne obchodzenie zamykania. Jednym podejściem jest sprawienie, aby jeden przewodnik nazwał te różne rzeczy, ale wymagałoby to trochę refaktoryzacji, której chciałbym uniknąć. Alternatywą jest dodanie pcntl_signal() wszędzie tam, gdzie jest to potrzebne, ale niestety wydaje się, że tylko jeden program obsługi może zostać zainstalowany na raz.

Jednak wygląda na to, że mogę być w stanie użyć register_shutdown_function(). To nie pułapka ^C lub inne sigs terminacji na własną rękę, a instrukcja jest jasne w tej kwestii:

funkcje wyłączania nie zostaną wykonane, jeśli proces zostanie zabity z SIGTERM lub SIGKILL sygnału

Co jest zaskakujące jest to, że znalazłem, że jeśli zatrudniam pcntl_signal() do zrobienia wyjścia, to procedury obsługi zamykania są rzeczywiście wywoływane. Ponadto, ponieważ można mieć wiele programów obsługi zamykania, to rozwiązuje to mój problem - każda klasa w moim kodzie, która chce z wdziękiem obsługiwać zakończenie, może przechwytywać i zarządzać własnym zamykaniem.

Moje pierwsze pytanie brzmi: dlaczego to działa? Próbowałem zarejestrować funkcję wyłączania bez obsługi sygnału, a to nie wydaje się być wywołane, jak mówi instrukcja. Domyślam się, że proces jest utrzymywany przy życiu przez PHP w celu obsługi sygnału, który powoduje wywołanie procedur obsługi shutdown?

Czy mogę polegać na tym zachowaniu, gdy podręcznik rzuca w to wątpliwość? Używam PHP 5.5 i nie chcę jeszcze uaktualnić do PHP7. Byłbym zainteresowany, czy to działa w wersjach 5.5 i 5.6, w różnych dystrybucjach.

Oczywiście, jeśli byłoby (lub nie byłoby) działa na 7.0 i/lub 7.1, to byłoby również interesujące - wiem jednak, że ticks will be handled differently w 7.1, więc istnieje większe prawdopodobieństwo, że ma to inne zachowanie .

+0

Zastanawiam się, po co właściwie odradzać się procesy PHP (biorąc pod uwagę, że nie losowo wyciekujesz pamięci ;-))? – bwoebi

+0

Po prostu dobra praktyka naprawdę, @bwoebi - daje mi pewność, że zawsze jest świeża instancja, nawet jeśli PHP i mój system są całkowicie wolne od wycieków ':-)'. – halfer

+0

Nie zgadzam się, że to naprawdę dobra praktyka. Dla twojego przypadku użycia może to być w porządku, ale gdy tylko będziesz potrzebował dostępnego na stałe gniazda [TCP/unix/...] (np. Do zrzucania informacji z programu na żywo), jest źle, gdy jest nieoczekiwanie niedostępny. – bwoebi

Odpowiedz

7

Funkcje wyłączania użytkownika PHP są wywoływane podczas zwykłego zakończenia procesu, w pewnym stopniu analogiczne do funkcji zarejestrowanych w atexit w innych językach programowania. Wyraźne exit lub niejawne wyjście na końcu skryptu jest zwykłym zakończeniem procesu.

Śmierć sygnału jest jednak nieprawidłowym zakończeniem procesu. Domyślnym zachowaniem dla SIGTERM (i obowiązkowego zachowania dla SIGKILL) jest natychmiastowe przerwanie uruchomionego procesu, bez uruchamiania jakichkolwiek procedur czyszczenia.

Po przechwyceniu SIGTERMa za pomocą pcntl_signal, użytkownik instaluje inne zachowanie i daje możliwość doświadczenia zwykłego zakończenia procesu.

Tak, możesz polegać na tym zachowaniu. Choć głównym instrukcja wpis nie jest wyraźne, reszta „Uwaga” cytujesz brzmi:

[Y] ou mogą korzystać pcntl_signal() zainstalować obsługi dla SIGTERM który wykorzystuje exit() do końca czysto.

+0

W porządku, dziękuję za to. Tak, widziałem tę notatkę, ale czytałem ją inaczej - "Funkcje wyłączania nie zostaną wykonane, jeśli proces zostanie zabity" jest dość jednoznaczne, ale może jest to tylko słabe sformułowanie. Późniejsza część notatki zdawała się sugerować, że należy korzystać z funkcji obsługi sygnałów zamiast obsługi wyłącznika. Twoja interpretacja doprowadziłaby mnie do przekonania, że ​​na to zachowanie można polegać również w PHP 7 - czy brzmi to dla ciebie poprawnie? – halfer

4

Oprócz pilcrows poprawna odpowiedź:

Obsługa sygnału jest konieczne do zmiany zachowań sygnału (czyli zamiast domyślnego działania). Cokolwiek się stanie po tym, nie ma znaczenia.

Możesz użyć funkcji obsługi sygnału, aby wykonać czyszczenie awaryjne, a następnie zadzwoń pod numer posix_kill(getmypid(), SIGKILL);, aby wymusić zakończenie bez czyszczenia. Jeśli tutaj jest exit(), zwykle uruchamiana jest sekwencja zamykania (tj. Wywoływanie wszystkich destruktorów, procedur obsługi zamykania itd.).

Kiedy mówisz oprócz, nie jesteś całkowicie poprawny. Należy użyć obsługi sygnału zamiast i może dodatkowo mają obsługiwać shutdown. [Odnosząc się do swojej comment]

Należy również pamiętać, że nazywając exit() po sekwencji zamykania został zainicjowany przerwie bieżące części (tj gdy jesteś obecnie wewnątrz obsługi zamykania i exit() jest wywoływana, wszystkie kolejne koparki z zamykaniem są ignorowane - ale np. nadal będą wywoływane destruktory). Idealnie masz jakieś sprawdzanie przeciwko temu:

register_shutdown_function(function() { $GLOBALS["#__in_shutdown"] = 1; }); 

i mają Sprawdź okolice exit():

if (empty($GLOBALS["#__in_shutdown"])) { 
    exit; 
} 

Tylko w przypadku, gdy chcesz być naprawdę bezpieczny, chyba że ktoś odpala SIGKILL na nim.

Należy zauważyć, że 7.0 lub 7.1 nie zmieni tego zachowania; wszystkie zmiany w 7.1 są declare(ticks=1); są zbędne, wystawiane zachowanie jest wciąż takie samo.

+0

Dobra dzięki.W odniesieniu do uwagi "dodawania", prawdopodobnie sformułowałem to źle - miałem na myśli, że oba są niezbędne, jeśli ktoś wyraźnie chce użyć procedur obsługi zamykania w celu uzyskania kilku wywołań zwrotnych w sytuacji sigterm/sigint. – halfer

+0

Aby dokładnie zamknąć każdą część mojego systemu, użyłbym obojętnego manipulatora sig, jak w oryginalnym poście, w skrypcie root, a następnie niezerowej liczbie procedur obsługi zamykania skonfigurowanych w różnych klasach, a żaden z programy obsługi zamykające same wywoływałyby 'exit()', pozostawiając go do obsługi sig. Czy to brzmi jak dobre podejście? – halfer

+0

Miałem na myśli dodanie if() wokół 'exit()' w procedurze obsługi sygnału, więc odbieranie SIGTERM, gdy jesteś już w sekwencji zamykania, nie przerwie jej. Co do reszty, tak, brzmi dobrze. – bwoebi

Powiązane problemy