Execute o script php como processo daemon

154

Eu preciso executar um script php como processo daemon (aguarde instruções e faça as coisas). O cron job não fará isso por mim, porque as ações precisam ser executadas assim que a instrução chegar. Eu sei que o PHP não é realmente a melhor opção para processos daemon devido a problemas de gerenciamento de memória, mas devido a várias razões, tenho que usar o PHP nesse caso. Me deparei com uma ferramenta da libslack chamada Daemon ( http://libslack.org/daemon ) que parece me ajudar a gerenciar processos daemon, mas não houve nenhuma atualização nos últimos 5 anos, por isso, gostaria de saber se você conhece algum outras alternativas adequadas ao meu caso. Qualquer informação será realmente apreciada.

Beier
fonte
2
por favor, verifique minha resposta. graças
Henrik P. Hessel
1
Me deparei com este post gonzalo123.com/2010/05/23/… que acredito ser relaxante e estável.
Teson
É muito fácil fazer com o systemd #
LeonanCarvalho

Respostas:

167

Você pode iniciar seu script php na linha de comando (ou seja, bash) usando

nohup php myscript.php &

o &coloca seu processo em segundo plano.

Edit:
Sim, existem algumas desvantagens, mas não é possível controlar? Isso está errado.
Um simples kill processidirá parar. E ainda é a melhor e mais simples solução.

Henrik P. Hessel
fonte
Se o terminal existir, o processo NÃO será encerrado. É por isso que o comando "nohup" está lá. Eu tenho usado um script PHP como daemon em todos os servidores como esse há anos. Pode haver uma solução melhor, mas esta é a mais rápida.
CDR
27
Isso não reiniciará o daemon se ele falhar, e não há uma maneira fácil de gerenciar o daemon.
Phil Wallach
6
Eu concordo com o que foi dito aqui-- essa é uma solução ruim. Você deve criar um script init por alguns motivos: 1) O script init é iniciado automaticamente na inicialização 2) Você pode gerenciar o daemon com os comandos start / stop / restart. Aqui está um exemplo de servefault: serverfault.com/questions/229759/…
Simian
1
ei galera ... parece-me nohupe &faz a mesma coisa: desanexar o processo iniciado da atual intenção do shell. Por que eu preciso dos dois? Não posso simplesmente fazer php myscript.php &ou nohup myscript.php? Graças
Nourdine
1
Se o script gravar no stdout (via echo ou var_dump), você poderá capturar essas informações com um arquivo de log como este:nohup php myscript.php > myscript.log &
Mischa
167

Outra opção é usar o Upstart . Ele foi originalmente desenvolvido para o Ubuntu (e vem com ele por padrão), mas destina-se a ser adequado para todas as distribuições do Linux.

Essa abordagem é semelhante ao Supervisord e daemontools , pois inicia automaticamente o daemon na inicialização do sistema e reaparece na conclusão do script.

Como configurá-lo:

Crie um novo arquivo de script em /etc/init/myphpworker.conf. Aqui está um exemplo:

# 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

Iniciando e parando seu daemon:

sudo service myphpworker start
sudo service myphpworker stop

Verifique se o seu daemon está em execução:

sudo service myphpworker status

obrigado

Um grande obrigado a Kevin van Zonneveld , de onde aprendi essa técnica.

Jonathan
fonte
2
amando isso. Apenas imaginando, é possível ter vários trabalhadores simultâneos? Só tenho o problema de que um trabalhador não é mais suficiente.
Manuel
1
isso será executado automaticamente na inicialização do sistema?
slier
2
Sudo "service myphpworker start" não funcionou para mim. Eu usei "sudo start myphpworker" e funciona perfeitamente
Matt Sich
3
@ Pradeepta Isso ocorre porque há um erro no post - não sei exatamente o que (e não testei isso), mas acho que sudo service myphpworker start/stop/statusfunciona apenas com serviços que /etc/init.dnão estão em serviços iniciantes. @ matt-sich parece ter descoberto a sintaxe correta. Outra opção é usar o Gearman ou o Resque, que permite o processamento em segundo plano e a desamonização.
ckm 29/07
3
Ubuntu em si está se movendo a usar systemd vez de arrivista: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai
72

Com o novo systemd, você pode criar um serviço.

Você deve criar um arquivo ou um link simbólico em /etc/systemd/system/, por exemplo. myphpdaemon.service e coloque conteúdo como este, myphpdaemon será o nome do serviço:

[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

Você poderá iniciar, obter status, reiniciar e interromper os serviços usando o comando

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

O script PHP deve ter um tipo de "loop" para continuar em execução.

<?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
        }
    }
}

Exemplo de trabalho:

[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

Se sua rotina PHP deve ser executada uma vez em um ciclo (como um resumo), você deve usar um script shell ou bash para ser chamado no arquivo de serviço systemd em vez do PHP diretamente, por exemplo:

#!/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

Se você escolher essa opção, altere o KillMode para mixedpara processos, bash (principal) e PHP (filho) sejam mortos.

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.

Nota: Toda vez que você altera seu "myphpdaemon.service", você deve executar o `systemctl daemon-reload ', mas se preocupe, se não o fizer, ele será alertado quando necessário.

LeonanCarvalho
fonte
7
Resposta subestimada. Você tem o meu +1.
Gergely Lukacsy
2
Impressionante. Gostaria que pudéssemos receber respostas do coração também, porque isso não deve ser enterrado nesta página.
Justin
1
Você deve verificar a systemctl status <your_service_name> -lsaída, isso lhe dará uma pista do que está acontecendo.
LeonanCarvalho
1
@LeandroTupone O MySQL e o Memcached foram uma demonstração de como usar dependências de serviço, não é necessário.
LeonanCarvalho
3
Isto realmente deve substituir a resposta vendo aceita como é agora 2019.
tempero
47

Se você puder - pegue uma cópia da Programação Avançada no ambiente UNIX . Todo o capítulo 13 é dedicado à programação de daemon. Os exemplos estão em C, mas toda a função que você precisa possui wrappers em PHP (basicamente as extensões pcntl e posix ).

Em poucas palavras - escrever um daemon (isso é possível apenas em sistemas operacionais baseados em * nix - o Windows usa serviços) é assim:

  1. Ligue umask(0)para evitar problemas de permissão.
  2. fork() e faça com que o pai saia.
  3. Ligue setsid().
  4. Configure o processamento do sinal de SIGHUP(geralmente isso é ignorado ou usado para sinalizar o daemon para recarregar sua configuração) e SIGTERM(para dizer ao processo para sair normalmente).
  5. fork() novamente e faça com que o pai saia.
  6. Altere o diretório de trabalho atual com chdir().
  7. fclose() stdin, stdoutE stderre não escrever para eles. A maneira correta é redirecioná-los para um /dev/nullou para um arquivo, mas não consegui encontrar uma maneira de fazê-lo no PHP. É possível quando você inicia o daemon para redirecioná-lo usando o shell (você terá que descobrir como fazer isso, não sei :).
  8. Você trabalha!

Além disso, como você está usando PHP, tenha cuidado com as referências cíclicas, pois o coletor de lixo do PHP, anterior ao PHP 5.3, não tem como coletar essas referências e o processo irá vazar na memória, até que eventualmente trava.

Emil Ivanov
fonte
1
Obrigado pela informação. Parece que o programa daemon do libslack praticamente faz toda a preparação como você mencionou. Acho que por enquanto vou continuar até encontrar outras boas alternativas.
Beier
1
Encontrei este post, o código esperado para copiar e colar em um aplicativo antigo e ruim que não fecha o stdin etc., ficou desapontado. : p
ThiefMaster 18/11/2013
1
Por que (5) fork () novamente?
TheFox
Trabalhou para mim - ótimo trabalho!
Gautam Sharma
Para futuros leitores perguntando por que bifurcar duas vezes: stackoverflow.com/questions/881388/… - TL; DR: Previne zumbis.
Ghedipunk
24

Eu corro um grande número de daemons PHP.

Concordo com você que o PHP não é a melhor (ou até boa) linguagem para fazer isso, mas os daemons compartilham código com os componentes voltados para a Web, portanto, no geral, é uma boa solução para nós.

Usamos daemontools para isso. É inteligente, limpo e confiável. De fato, nós o usamos para executar todos os nossos daemons.

Você pode verificar isso em http://cr.yp.to/daemontools.html .

EDIT: Uma lista rápida de recursos.

  • Inicia automaticamente o daemon na reinicialização
  • Reiniciar automaticamente o dameon em caso de falha
  • O registro é tratado para você, incluindo sobreposição e remoção
  • Interface de gerenciamento: 'svc' e 'svstat'
  • Compatível com UNIX (talvez não seja uma vantagem para todos)
Phil Wallach
fonte
Também instalável a partir dos repositórios, por exemplo, no apt!
Kzqai
14

Você pode

  1. Use nohupcomo Henrik sugeriu.
  2. Use screene execute seu programa PHP como um processo regular dentro dele. Isso lhe dá mais controle do que usar nohup.
  3. Use um daemonizer como http://supervisord.org/ (está escrito em Python, mas pode daemonizar qualquer programa de linha de comando e fornecer um controle remoto para gerenciá-lo).
  4. Escreva seu próprio wrapper daemonise como sugerido por Emil, mas é um IMO exagerado.

Eu recomendaria o método mais simples (tela na minha opinião) e, se desejar mais recursos ou funcionalidades, passe para métodos mais complexos.

Noufal Ibrahim
fonte
Você poderia fornecer uma configuração de supervisão semelhante?
Alix Axel
11

Há mais de uma maneira de resolver esse problema.

Eu não sei os detalhes, mas talvez haja outra maneira de acionar o processo PHP. Por exemplo, se você precisar que o código seja executado com base em eventos em um banco de dados SQL, poderá configurar um gatilho para executar seu script. Isso é realmente fácil de fazer no PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html .

Honestamente, acho que sua melhor aposta é criar um processo Damon usando nohup. nohup permite que o comando continue executando mesmo após o logout do usuário:

nohup php myscript.php &

Existe, no entanto, um problema muito sério. Como você disse que o gerenciador de memória do PHP é um lixo completo, ele foi construído com a suposição de que um script está sendo executado apenas por alguns segundos e depois existe. Seu script PHP começará a usar GIGABYTES de memória após apenas alguns dias. Você DEVE TAMBÉM criar um script cron que seja executado a cada 12 ou talvez 24 horas que mata e gera novamente seu script php assim:

killall -3 php
nohup php myscript.php &

Mas e se o script estivesse no meio de um trabalho? Bem, kill -3 é uma interrupção, é o mesmo que fazer um ctrl + c na CLI. Seu script php pode capturar essa interrupção e sair normalmente usando a biblioteca pcntl do PHP: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Aqui está um exemplo:

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

A idéia por trás do $ lock é que o script PHP pode abrir um arquivo com um fopen ("file", "w") ;. Somente um processo pode ter um bloqueio de gravação em um arquivo; portanto, usando isso, você pode garantir que apenas uma cópia do seu script PHP esteja em execução.

Boa sorte!

torre
fonte
6

Confira https://github.com/shaneharter/PHP-Daemon

Esta é uma biblioteca de daemon orientada a objetos. Ele possui suporte interno para itens como log e recuperação de erros, além de criar trabalhadores em segundo plano.

Shane H
fonte
3

Recentemente, tive a necessidade de uma solução de plataforma cruzada (Windows, Mac e Linux) para o problema de executar scripts PHP como daemons. Resolvi o problema escrevendo minha própria solução baseada em C ++ e criando binários:

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

Suporte completo para Linux (via sysvinit), mas também serviços do Windows NT e Mac OSX launchd.

Se você só precisa do Linux, algumas das outras soluções apresentadas aqui funcionam bem o suficiente e, dependendo do sabor. Atualmente, também existe o Upstart e o systemd, que têm fallbacks para scripts sysvinit. Mas metade do ponto de usar o PHP é que ele é multiplataforma, por isso o código escrito na linguagem tem uma boa chance de trabalhar em todos os lugares como está. As deficiências começam a aparecer quando certos aspectos externos do nível do SO nativo entram em cena, como serviços do sistema, mas você terá esse problema com a maioria das linguagens de script.

Tentar captar sinais como alguém sugerido aqui na área de usuários do PHP não é uma boa idéia. Leia a documentação com pcntl_signal()atenção e você aprenderá rapidamente que o PHP lida com sinais usando alguns métodos bastante desagradáveis ​​(especificamente, 'ticks') que produzem vários ciclos por algo raramente visto pelos processos (ou seja, sinais). A manipulação de sinais no PHP também não está disponível apenas nas plataformas POSIX e o suporte difere com base na versão do PHP. Inicialmente, parece uma solução decente, mas fica muito aquém de ser realmente útil.

O PHP também está melhorando com relação a problemas de vazamento de memória à medida que o tempo avança. Você ainda precisa ter cuidado (o analisador DOM XML tende a vazar ainda), mas raramente vejo processos descontrolados hoje em dia e o rastreador de erros do PHP é bastante silencioso em comparação com os dias de outrora.

CubicleSoft
fonte
1

Como outros já mencionaram, executar o PHP como um daemon é bastante fácil e pode ser feito usando uma única linha de comando. Mas o problema real é mantê-lo funcionando e gerenciando-o. Eu já tive o mesmo problema há algum tempo e, embora já existam muitas soluções disponíveis, a maioria delas possui muitas dependências ou é difícil de usar e não é adequada para usos básicos. Eu escrevi um shell script que pode gerenciar qualquer processo / aplicativo, incluindo scripts cli PHP. Pode ser definido como um cronjob para iniciar o aplicativo e conterá o aplicativo e gerenciá-lo. Se for executado novamente, por exemplo, através do mesmo cronjob, ele verifica se o aplicativo está em execução ou não, se simplesmente sai e deixa sua instância anterior continuar gerenciando o aplicativo.

Fiz o upload para o github, fique à vontade para usá-lo: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Simplesmente vigia seu aplicativo (iniciar, reiniciar, registrar, monitorar etc.). um script genérico para garantir que seu aplicativo continue sendo executado corretamente. Intencionalmente, ele usa o nome do processo em vez do arquivo pid / lock para evitar todos os seus efeitos colaterais e manter o script o mais simples e rápido possível, para que ele sempre funcione mesmo quando o EasyDaemonizer for reiniciado. Recursos

  • Inicia o aplicativo e, opcionalmente, um atraso personalizado para cada início
  • Garante que apenas uma instância esteja em execução
  • Monitora o uso da CPU e reinicia o aplicativo automaticamente quando atinge o limite definido
  • Configurando o EasyDeamonizer para executar via cron para executá-lo novamente se for interrompido por qualquer motivo
  • Registra sua atividade
Sina Salek
fonte
1

Estendendo a resposta de Emil Ivaov , você pode fazer o seguinte para fechar STDIN, STDOUT AND STDERROR em 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');

Basicamente, você fecha os fluxos padrão para que o PHP não tenha lugar para escrever. As seguintes fopenchamadas definirão o IO padrão para /dev/null.

Eu li isso no livro de Rob Aley - PHP além da web

Raheel
fonte
0

Eu escrevi e implantei um simples php-daemon, o código está online aqui

https://github.com/jmullee/PhpUnixDaemon

Características: eliminação de privilégios, manipulação de sinal, registro

Usei-o em um manipulador de filas (caso de uso: aciona uma operação demorada a partir de uma página da web, sem fazer com que o php gerador de páginas aguarde, ou seja, inicie uma operação assíncrona) https://github.com/jmullee/PhpIPCMessageQueue

jmullee
fonte
0

você pode verificar o pm2 aqui, http://pm2.keymetrics.io/

crie um arquivo ssh, como o worker.sh, inserido no seu script php com o qual você irá lidar.

worker.sh

php /path/myscript.php

início de daemon

pm2 start worker.sh

Saúde, é isso.

Serkan Koch
fonte