Uruchom skrypt php jako proces demona


154

Muszę uruchomić skrypt php jako proces demona (czekaj na instrukcje i rób rzeczy). cron nie zrobi tego za mnie, ponieważ działania muszą zostać podjęte, gdy tylko nadejdzie instrukcja. Wiem, że PHP nie jest najlepszą opcją dla procesów demonów ze względu na problemy z zarządzaniem pamięcią, ale z różnych powodów muszę w tym przypadku używać PHP. Natknąłem się na narzędzie autorstwa libslack o nazwie Daemon ( http://libslack.org/daemon ), które wydaje się pomagać mi w zarządzaniu procesami demona, ale nie było żadnych aktualizacji w ciągu ostatnich 5 lat, więc zastanawiam się, czy znasz jakieś inne alternatywy odpowiednie dla mojego przypadku. Każda informacja będzie naprawdę mile widziana.


2
proszę sprawdzić moją odpowiedź. dzięki
Henrik P. Hessel


1
Natknąłem się na ten post gonzalo123.com/2010/05/23/…, który moim zdaniem jest zarówno relaksujący, jak i stabilny.
Teson

Bardzo łatwo to zrobić dzięki systemd
LeonanCarvalho

Odpowiedzi:


167

Możesz uruchomić swój skrypt php z wiersza poleceń (np. Bash) za pomocą

nohup php myscript.php &

&stawia proces w tle.

Edycja:
Tak, są pewne wady, ale nie można ich kontrolować? Po prostu źle.
Prosty kill processidto powstrzyma. I nadal jest to najlepsze i najprostsze rozwiązanie.


Jeśli terminal istnieje, proces NIE zakończy się. Dlatego istnieje polecenie „nohup”. Od lat używam skryptu PHP jako demona na wszystkich serwerach, takich jak ten. Może być lepsze rozwiązanie, ale to najszybsze.
CDR

27
Nie spowoduje to ponownego uruchomienia demona, jeśli zawiedzie, i nie ma w ogóle łatwego sposobu zarządzania demonem.
Phil Wallach,

6
Zgadzam się z tym, co zostało tutaj powiedziane - to jest złe rozwiązanie. Powinieneś utworzyć skrypt inicjujący z kilku powodów: 1) Skrypt inicjujący jest uruchamiany automatycznie przy starcie 2) Możesz zarządzać demonem za pomocą poleceń start / stop / restart. Oto przykład z servefault: serverfault.com/questions/229759/ ...
Simian

1
Hej chłopaki ... wydaje mi się, nohupi &robi to samo samą rzecz: rozpoczęto proces odłączania od obecnego intance skorupy. Dlaczego potrzebuję ich obu? Czy nie mogę po prostu zrobić, php myscript.php &czy nohup myscript.php?? Dzięki
nourdine

1
Jeśli skrypt zapisuje na stdout (przez echo lub var_dump), możesz przechwycić te informacje za pomocą pliku dziennika takiego:nohup php myscript.php > myscript.log &
Mischa,

167

Inną opcją jest użycie Upstart . Został pierwotnie opracowany dla Ubuntu (i jest domyślnie dostarczany z nim w pakiecie), ale ma być odpowiedni dla wszystkich dystrybucji Linuksa.

Podejście to jest podobne do Supervisord i daemontools , ponieważ automatycznie uruchamia demona przy starcie systemu i odradza się po zakończeniu skryptu.

Jak to ustawić:

Utwórz nowy plik skryptu pod adresem /etc/init/myphpworker.conf. Oto przykład:

# Info
description "My PHP Worker"
author      "Jonathan"

# Events
start on startup
stop on shutdown

# Automatically respawn
respawn
respawn limit 20 5

# Run the script!
# Note, in this example, if your PHP script returns
# the string "ERROR", the daemon will stop itself.
script
    [ $(exec /usr/bin/php -f /path/to/your/script.php) = 'ERROR' ] && ( stop; exit 1; )
end script

Uruchamianie i zatrzymywanie demona:

sudo service myphpworker start
sudo service myphpworker stop

Sprawdź, czy Twój demon jest uruchomiony:

sudo service myphpworker status

Dzięki

Wielkie podziękowania dla Kevina van Zonnevelda , od którego nauczyłem się tej techniki.


2
kocham to. Zastanawiasz się tylko, czy można mieć wielu równoległych pracowników? Mam tylko taki problem, że jeden pracownik już nie wystarcza.
Manuel

1
czy będzie to automatycznie uruchamiane przy starcie systemu?
wolniejszy

2
Sudo „service myphpworker start” nie działa dla mnie. Użyłem „sudo start myphpworker” i działa idealnie
Matt Sich

3
@Pradeepta To dlatego, że w poście jest błąd - nie jestem dokładnie pewien co (i nie testowałem tego), ale myślę, że sudo service myphpworker start/stop/statusdziała to tylko z usługami, które nie są /etc/init.dusługami początkowymi. Wydaje się, że @ matt-sich odkrył poprawną składnię. Inną opcją jest użycie Gearman lub Resque, które umożliwiają przetwarzanie w tle i deamonizację.
ckm

3
Ubuntu sama porusza Korzystanie Systemd zamiast nowobogackich: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai

72

Dzięki nowemu systemowi możesz stworzyć usługę.

Musisz utworzyć plik lub dowiązania w /etc/systemd/system/, np. myphpdaemon.service i umieść zawartość taką jak ta, myphpdaemon będzie nazwą usługi:

[Unit]
Description=My PHP Daemon Service
#May your script needs MySQL or other services to run, eg. MySQL Memcached
Requires=mysqld.service memcached.service 
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/myphpdaemon.pid
ExecStart=/usr/bin/php -f /srv/www/myphpdaemon.php arg1 arg2> /dev/null 2>/dev/null
#ExecStop=/bin/kill -HUP $MAINPID #It's the default you can change whats happens on stop command
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

Restart=on-failure
RestartSec=42s

StandardOutput=null #If you don't want to make toms of logs you can set it null if you sent a file or some other options it will send all php output to this one.
StandardError=/var/log/myphpdaemon.log
[Install]
WantedBy=default.target

Będziesz mógł uruchomić, uzyskać status, zrestartować i zatrzymać usługi za pomocą polecenia

systemctl <start|status|restart|stop|enable> myphpdaemon

Skrypt PHP powinien mieć rodzaj „pętli”, aby kontynuować działanie.

<?php
gc_enable();//
while (!connection_aborted() || PHP_SAPI == "cli") {

  //Code Logic

  //sleep and usleep could be useful
    if (PHP_SAPI == "cli") {
        if (rand(5, 100) % 5 == 0) {
            gc_collect_cycles(); //Forces collection of any existing garbage cycles
        }
    }
}

Przykład pracy:

[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/php_app_sync.pid
ExecStart=/bin/sh -c '/usr/bin/php -f /var/www/app/private/server/cron/app_sync.php  2>&1 > /var/log/app_sync.log'
KillMode=mixed

Restart=on-failure
RestartSec=42s

[Install]
WantedBy=default.target

Jeśli twoja procedura PHP powinna być wykonywana raz w cyklu (jak diggest), możesz użyć powłoki lub skryptu bash do wywołania w pliku usługi systemd zamiast bezpośrednio PHP, na przykład:

#!/usr/bin/env bash
script_path="/app/services/"

while [ : ]
do
#    clear
    php -f "$script_path"${1}".php" fixedparameter ${2}  > /dev/null 2>/dev/null
    sleep 1
done

Jeśli wybrałeś tę opcję, powinieneś zmienić KillMode na mixedprocesy, zabić bash (main) i PHP (child).

ExecStart=/app/phpservice/runner.sh phpfile parameter  > /dev/null 2>/dev/null
KillMode=process

This method also is effective if you're facing a memory leak.

Uwaga: Za każdym razem, gdy zmieniasz "myphpdaemon.service", musisz uruchomić `systemctl daemon-reload ', ale martw się, jeśli tego nie zrobisz, zostanie on ostrzeżony, gdy będzie potrzebny.


7
Niedoceniona odpowiedź. Masz moje +1.
Gergely Lukacsy

2
Niesamowite. Żałuję, że nie mamy też odpowiedzi na serce, ponieważ nie powinno się tego ukrywać na tej stronie.
Justin

1
Powinieneś sprawdzić systemctl status <your_service_name> -lwynik, to da ci wskazówkę, co się dzieje.
LeonanCarvalho

1
@LeandroTupone MySQL i Memcached były demonstracją, jak korzystać z zależności usług, co nie jest konieczne.
LeonanCarvalho

3
To powinno naprawdę zastąpić zaakceptowaną odpowiedź, ponieważ jest teraz 2019.
spice

47

Jeśli możesz - pobierz kopię Zaawansowanego programowania w środowisku UNIX . Cały rozdział 13 jest poświęcony programowaniu demonów. Przykłady są w C, ale wszystkie potrzebne funkcje mają opakowania w PHP (w zasadzie rozszerzenia pcntl i posix ).

W kilku słowach - pisanie demona (jest to możliwe tylko w systemach operacyjnych opartych na * nix - Windows korzysta z usług) wygląda tak:

  1. Zadzwoń, umask(0)aby zapobiec problemom z pozwoleniami.
  2. fork() i wyprowadzić rodzica.
  3. Zadzwoń setsid().
  4. Przetwarzanie sygnału konfiguracji SIGHUP(zwykle jest to ignorowane lub używane do sygnalizowania demonowi ponownego załadowania konfiguracji) i SIGTERM(aby nakazać procesowi prawidłowe zakończenie).
  5. fork() ponownie i pozwól rodzicowi wyjść.
  6. Zmień bieżący katalog roboczy za pomocą chdir().
  7. fclose() stdin, stdoutA stderri nie pisać do nich. Poprawnym sposobem jest przekierowanie ich do jednego z nich /dev/nulllub do pliku, ale nie mogłem znaleźć sposobu, aby to zrobić w PHP. Po uruchomieniu demona jest to możliwe, aby przekierować je za pomocą powłoki (sam musisz się dowiedzieć, jak to zrobić, nie wiem :).
  8. Wykonuj swoją pracę!

Ponadto, ponieważ używasz PHP, uważaj na cykliczne odwołania, ponieważ garbage collector PHP, przed PHP 5.3, nie ma możliwości zbierania tych odwołań i proces będzie przeciekał pamięć, aż w końcu ulegnie awarii.


1
Dzięki za informację. Wygląda na to, że demon libslack prawie wykonuje całą pracę przygotowawczą, o której wspomniałeś. Myślę, że na razie się tego trzymam, dopóki nie znajdę innych dobrych alternatyw.
Beier

1
Znalazłem ten post, oczekiwano, że kod do skopiowania i wklejenia do kiepskiej starej aplikacji, która nie zamyka standardowego wejścia itp., Był rozczarowany. : p
ThiefMaster

1
Dlaczego znowu (5) fork ()?
TheFox

Pracował dla mnie - świetna robota!
Gautam Sharma

Dla przyszłych czytelników pytających, dlaczego rozwidlać się dwa razy: stackoverflow.com/questions/881388/… - TL; DR: zapobiega zombie.
Ghedipunk

24

Uruchamiam wiele demonów PHP.

Zgadzam się z Tobą, że PHP nie jest najlepszym (a nawet dobrym) językiem do tego celu, ale demony współdzielą kod z komponentami internetowymi, więc ogólnie jest to dla nas dobre rozwiązanie.

Używamy do tego daemontools. Jest inteligentny, czysty i niezawodny. W rzeczywistości używamy go do uruchamiania wszystkich naszych demonów.

Możesz to sprawdzić pod adresem http://cr.yp.to/daemontools.html .

EDYCJA: krótka lista funkcji.

  • Automatycznie uruchamia demona przy ponownym uruchomieniu
  • Automatycznie uruchom ponownie dameon w przypadku awarii
  • Rejestrowanie jest obsługiwane za Ciebie, w tym przenoszenie i przycinanie
  • Interfejs zarządzania: „svc” i „svstat”
  • Przyjazny dla UNIX (być może nie jest to plus dla każdego)

Można również zainstalować z repozytoriów, np. W apt!
Kzqai,

14

Możesz

  1. Użyj nohupzgodnie z sugestią Henrika.
  2. Używaj screeni uruchamiaj swój program PHP jako zwykły proces wewnątrz tego. Daje to większą kontrolę niż używanie nohup.
  3. Użyj daemonizera, takiego jak http://supervisord.org/ (jest napisany w Pythonie, ale może demonizować każdy program wiersza poleceń i zapewniać zdalną kontrolę do zarządzania nim).
  4. Napisz własne opakowanie daemonise, jak sugerował Emil, ale to przesada IMO.

Poleciłbym najprostszą metodę (moim zdaniem ekran), a następnie, jeśli chcesz mieć więcej funkcji lub funkcjonalności, przejdź do bardziej złożonych metod.


Czy możesz podać podobną konfigurację nadzorcy?
Alix Axel

11

Jest więcej niż jeden sposób rozwiązania tego problemu.

Nie znam szczegółów, ale być może jest inny sposób na uruchomienie procesu PHP. Na przykład, jeśli chcesz, aby kod był uruchamiany na podstawie zdarzeń w bazie danych SQL, możesz skonfigurować wyzwalacz do wykonania skryptu. W PostgreSQL jest to naprawdę łatwe: http://www.postgresql.org/docs/current/static/external-pl.html .

Szczerze mówiąc, myślę, że najlepiej jest stworzyć proces Damona za pomocą nohup. nohup pozwala na kontynuowanie wykonywania polecenia nawet po wylogowaniu użytkownika:

nohup php myscript.php &

Jest jednak bardzo poważny problem. Jak powiedziałeś, menedżer pamięci PHP jest kompletnym śmieciem, został zbudowany przy założeniu, że skrypt jest wykonywany tylko przez kilka sekund, a potem istnieje. Twój skrypt PHP zacznie używać GIGABYTÓW pamięci już po kilku dniach. MUSISZ TAKŻE utworzyć skrypt cron, który będzie uruchamiany co 12, a może 24 godziny, który zabija i ponownie odradza twój skrypt php w następujący sposób:

killall -3 php
nohup php myscript.php &

Ale co, jeśli scenariusz był w trakcie pracy? Cóż, kill -3 to przerwanie, to to samo, co wykonanie ctrl + c w CLI. Twój skrypt php może przechwycić to przerwanie i wyjść z wdziękiem za pomocą biblioteki PHP pcntl: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Oto przykład:

function clean_up() {
  GLOBAL $lock;
  mysql_close();
  fclose($lock)
  exit();
}
pcntl_signal(SIGINT, 'clean_up');

Ideą $ lock jest to, że skrypt PHP może otworzyć plik z fopen ("plik", "w") ;. Tylko jeden proces może mieć blokadę zapisu w pliku, więc używając tego możesz upewnić się, że działa tylko jedna kopia twojego skryptu PHP.

Powodzenia!




3

Niedawno potrzebowałem rozwiązania wieloplatformowego (Windows, Mac i Linux), aby rozwiązać problem uruchamiania skryptów PHP jako demonów. Rozwiązałem problem, pisząc własne rozwiązanie oparte na C ++ i tworząc pliki binarne:

https://github.com/cubiclesoft/service-manager/

Pełne wsparcie dla Linuksa (przez sysvinit), ale także dla usług Windows NT i Mac OSX.

Jeśli potrzebujesz tylko Linuksa, kilka innych przedstawionych tutaj rozwiązań działa wystarczająco dobrze i, w zależności od smaku. Obecnie istnieje również wersja Upstart i systemd, która ma rozwiązania awaryjne dla skryptów sysvinit. Ale połowa sensu używania PHP polega na tym, że ma on charakter wieloplatformowy, więc kod napisany w tym języku ma całkiem spore szanse na działanie wszędzie tak, jak jest. Niedociągnięcia zaczynają się pojawiać, gdy pojawiają się pewne zewnętrzne natywne aspekty poziomu systemu operacyjnego, takie jak usługi systemowe, ale ten problem wystąpi w przypadku większości języków skryptowych.

Próba złapania sygnałów, jak ktoś zasugerował tutaj w przestrzeni użytkownika PHP, nie jest dobrym pomysłem. Przeczytaj pcntl_signal()uważnie dokumentację, a szybko przekonasz się, że PHP obsługuje sygnały przy użyciu raczej nieprzyjemnych metod (w szczególności „tików”), które żują kilka cykli na coś rzadko spotykanego w procesach (np. Sygnały). Obsługa sygnałów w PHP jest również ledwo dostępna na platformach POSIX, a obsługa różni się w zależności od wersji PHP. Początkowo brzmi to jak przyzwoite rozwiązanie, ale nie jest naprawdę przydatne.

W miarę upływu czasu PHP coraz lepiej radzi sobie z problemami z wyciekiem pamięci. Nadal musisz być ostrożny (parser DOM XML wciąż przecieka), ale w dzisiejszych czasach rzadko widzę niekontrolowane procesy, a moduł śledzenia błędów PHP jest dość cichy w porównaniu do dawnych czasów.


1

Jak już wspominali inni, uruchomienie PHP jako demona jest dość łatwe i można to zrobić za pomocą jednej linii poleceń. Ale rzeczywisty problem polega na tym, że działa i zarządza nim. Miałem ten sam problem już jakiś czas temu i chociaż dostępnych jest już wiele rozwiązań, większość z nich ma wiele zależności lub są trudne w użyciu i nie nadają się do podstawowych zastosowań. Napisałem skrypt powłoki, który może zarządzać dowolnym procesem / aplikacją, w tym skryptami PHP CLI. Można go ustawić jako cronjob do uruchamiania aplikacji i będzie zawierać aplikację i nią zarządzać. Jeśli zostanie uruchomiony ponownie, na przykład za pomocą tego samego cronjob, sprawdza, czy aplikacja działa, czy nie, a jeśli tak, po prostu zamyka się i pozwala jej poprzedniej instancji kontynuować zarządzanie aplikacją.

Wgrałem go na github, nie krępuj się go używać: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Po prostu czuwa nad aplikacją (uruchamianie, restart, logowanie, monitorowanie itp.). ogólny skrypt, aby upewnić się, że aplikacja działa poprawnie. Celowo używa nazwy procesu podczas wczytywania pliku pid / lock, aby zapobiec wszystkim jego efektom ubocznym i zachować jak najprostszy i możliwie najprostszy skrypt, dzięki czemu zawsze działa nawet po ponownym uruchomieniu EasyDaemonizer. cechy

  • Uruchamia aplikację i opcjonalnie niestandardowe opóźnienie dla każdego uruchomienia
  • Zapewnia, że ​​działa tylko jedna instancja
  • Monitoruje użycie procesora i automatycznie uruchamia aplikację ponownie po osiągnięciu zdefiniowanego progu
  • Ustawienie EasyDeamonizer, aby uruchamiał się przez cron, aby uruchomić go ponownie, jeśli zostanie zatrzymany z jakiegokolwiek powodu
  • Rejestruje swoją aktywność

1

Rozszerzając odpowiedź Emila Ivaova , możesz wykonać następujące czynności, aby zamknąć STDIN, STDOUT i STDERROR w php

if (!fclose(STDIN)) {
    exit("Could not close STDIN");
}

if (!fclose(STDOUT)) {
    exit("Could not close STDOUT");
}

if (!fclose(STDERR)) {
    exit("Could not close STDERR");
}

$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('/dev/null', 'w');
$STDERR = fopen('/var/log/our_error.log', 'wb');

Zasadniczo zamykasz standardowe strumienie, aby PHP nie miało gdzie pisać. Następujące fopenwywołania ustawią standardowe We / Wy na /dev/null.

Przeczytałem to z książki Roba Aleya - PHP poza siecią



0

możesz sprawdzić pm2 tutaj: http://pm2.keymetrics.io/

utwórz plik ssh, taki jak worker.sh i umieść w swoim skrypcie php, którym będziesz się zajmować.

worker.sh

php /path/myscript.php

Demon start

pm2 start worker.sh

Na zdrowie, to wszystko.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.