Eu trabalho em um aplicativo da Web um tanto grande, e o back-end é principalmente em PHP. Existem vários lugares no código em que preciso concluir alguma tarefa, mas não quero fazer o usuário esperar pelo resultado. Por exemplo, ao criar uma nova conta, preciso enviar a eles um email de boas-vindas. Mas quando eles apertam o botão 'Concluir registro', não quero fazê-los esperar até que o email seja realmente enviado, só quero iniciar o processo e retornar uma mensagem ao usuário imediatamente.
Até agora, em alguns lugares, tenho usado o que parece um hack com exec (). Basicamente, fazendo coisas como:
exec("doTask.php $arg1 $arg2 $arg3 >/dev/null 2>&1 &");
O que parece funcionar, mas estou me perguntando se existe uma maneira melhor. Estou pensando em escrever um sistema que enfileire tarefas em uma tabela MySQL e um script PHP de execução longa separado que consulta essa tabela uma vez por segundo e executa as novas tarefas que encontrar. Isso também teria a vantagem de me permitir dividir as tarefas entre várias máquinas operadoras no futuro, se eu precisasse.
Estou reinventando a roda? Existe uma solução melhor do que o exec () hack ou a fila do MySQL?
fonte
Quando você deseja apenas executar uma ou várias solicitações HTTP sem precisar esperar pela resposta, também existe uma solução PHP simples.
No script de chamada:
No script.php chamado, você pode chamar estas funções PHP nas primeiras linhas:
Isso faz com que o script continue em execução sem limite de tempo quando a conexão HTTP for fechada.
fonte
Outra maneira de dividir os processos é via curl. Você pode configurar suas tarefas internas como um serviço da web. Por exemplo:
Em seguida, nos scripts acessados pelo usuário, faça chamadas para o serviço:
Seu serviço pode acompanhar a fila de tarefas com o mysql ou o que você quiser: está tudo encerrado no serviço e seu script está consumindo apenas URLs. Isso libera você para mover o serviço para outra máquina / servidor, se necessário (ou seja, facilmente escalável).
Adicionar autorização http ou um esquema de autorização personalizado (como os serviços da Web da Amazon) permite abrir suas tarefas para serem consumidas por outras pessoas / serviços (se você quiser) e você pode ir além e adicionar um serviço de monitoramento no topo para acompanhar status da fila e da tarefa.
É preciso um pouco de trabalho de configuração, mas há muitos benefícios.
fonte
Eu usei o Beanstalkd em um projeto e planejei novamente. Descobri que é uma excelente maneira de executar processos assíncronos.
Algumas coisas que fiz com isso são:
Eu escrevi um sistema baseado no Zend-Framework para decodificar um URL 'legal', por exemplo, para redimensionar uma imagem que ele chamaria
QueueTask('/image/resize/filename/example.jpg')
. A URL foi decodificada primeiro em uma matriz (módulo, controlador, ação, parâmetros) e depois convertida em JSON para injeção na própria fila.Um script cli de longa duração pegou o trabalho na fila, executou-o (via Zend_Router_Simple) e, se necessário, colocou informações em memcached para que o site PHP pegasse conforme necessário quando terminado.
Uma das rugas que eu também coloquei foi que o cli-script rodava apenas 50 loops antes de reiniciar, mas se quisesse reiniciar como planejado, o faria imediatamente (sendo executado através de um script bash). Se houvesse um problema e eu o fizesse
exit(0)
(o valor padrão paraexit;
oudie();
), ele primeiro seria pausado por alguns segundos.fonte
Se for apenas uma questão de fornecer tarefas caras, no caso de php-fpm ser suportado, por que não usar a
fastcgi_finish_request()
função?Você realmente não usa assincronismo desta maneira:
fastcgi_finish_request()
.Mais uma vez é necessário php-fpm.
fonte
Aqui está uma classe simples que eu codifiquei para meu aplicativo da web. Permite bifurcar scripts PHP e outros scripts. Funciona em UNIX e Windows.
fonte
Esse é o mesmo método que venho usando há alguns anos e não vi nem encontrei nada melhor. Como as pessoas disseram, o PHP é de thread único, então não há muito mais que você possa fazer.
Na verdade, adicionei um nível extra a isso, que é obter e armazenar a identificação do processo. Isso permite que eu redirecione para outra página e faça com que o usuário fique nessa página, usando o AJAX para verificar se o processo está completo (o ID do processo não existe mais). Isso é útil nos casos em que o tamanho do script levaria o tempo limite ao navegador, mas o usuário precisa aguardar a conclusão do script antes da próxima etapa. (No meu caso, estava processando grandes arquivos ZIP com arquivos CSV, que adicionam até 30.000 registros ao banco de dados, após o qual o usuário precisa confirmar algumas informações.)
Eu também usei um processo semelhante para a geração de relatórios. Não tenho certeza se usaria o "processamento em segundo plano" para algo como um email, a menos que haja um problema real com um SMTP lento. Em vez disso, posso usar uma tabela como uma fila e, em seguida, ter um processo que é executado a cada minuto para enviar os e-mails dentro da fila. Você precisaria ter o cuidado de enviar e-mails duas vezes ou outros problemas semelhantes. Eu consideraria um processo de fila semelhante para outras tarefas também.
fonte
O PHP possui multithreading, por padrão não está ativado, existe uma extensão chamada pthreads que faz exatamente isso. Você precisará de php compilado com o ZTS. (Thread Safe) Links:
Exemplos
Outro tutorial
Extensão PECL pthreads
fonte
É uma ótima idéia usar o cURL, conforme sugerido pela rojoca.
Aqui está um exemplo. Você pode monitorar o texto.txt enquanto o script está sendo executado em segundo plano:
fonte
Infelizmente, o PHP não possui nenhum tipo de recurso de encadeamento nativo. Então, acho que nesse caso você não tem escolha a não ser usar algum tipo de código personalizado para fazer o que deseja.
Se você pesquisar na Internet por coisas de encadeamento de PHP, algumas pessoas terão maneiras de simular threads no PHP.
fonte
Se você definir o cabeçalho HTTP de comprimento de conteúdo na resposta "Obrigado por registrar", o navegador deverá fechar a conexão após o recebimento do número especificado de bytes. Isso deixa o processo do servidor em execução (assumindo que ignore_user_abort está definido) para que ele possa terminar de trabalhar sem fazer com que o usuário final espere.
É claro que você precisará calcular o tamanho do seu conteúdo de resposta antes de renderizar os cabeçalhos, mas isso é muito fácil para respostas curtas (gravar saída em uma string, chamar strlen (), chamar header (), chamar header (), render string).
Essa abordagem tem a vantagem de não forçá-lo a gerenciar uma fila de "front-end" e, embora seja necessário fazer algum trabalho no back-end para impedir que os processos filhos de corrida HTTP se interajam, isso é algo que você já precisava fazer , de qualquer forma.
fonte
header('Content-Length: 3'); echo '1234'; sleep(5);
, mesmo que o navegador leve apenas 3 caracteres, ele ainda aguarda 5 segundos antes de mostrar a resposta. o que estou perdendo?phpinfo()
. A única outra coisa que eu poderia imaginar é que preciso atingir um tamanho mínimo de buffer primeiro, por exemplo, 256 ou mais bytes.Se você não deseja o ActiveMQ completo, recomendo considerar o RabbitMQ . O RabbitMQ é um sistema de mensagens leve que usa o padrão AMQP .
Eu recomendo também examinar o php-amqplib - uma popular biblioteca cliente do AMQP para acessar os intermediários de mensagens baseados no AMQP.
fonte
Eu acho que você deve tentar esta técnica, ajudará a chamar quantas páginas você gosta, todas as páginas serão executadas de uma vez independentemente, sem esperar por cada resposta da página como assíncrona.
cornjobpage.php // página principal
testpage.php
PS: se você deseja enviar parâmetros de URL como loop, siga esta resposta: https://stackoverflow.com/a/41225209/6295712
fonte
Gerar novos processos no servidor usando
exec()
ou diretamente em outro servidor usando curl não é tão bom assim: se formos executivos, você basicamente estará preenchendo o servidor com processos de execução longa que podem ser gerenciados por outros servidores que não são da Web, e usar curl vincula outro servidor, a menos que você crie algum tipo de balanceamento de carga.Eu usei o Gearman em algumas situações e acho melhor para esse tipo de caso de uso. Posso usar um único servidor de fila de tarefas para lidar basicamente com o enfileiramento de todas as tarefas que precisam ser executadas pelo servidor e ativar servidores de trabalho, cada um dos quais pode executar quantas instâncias do processo de trabalho forem necessárias e aumentar o número de servidores de trabalho, conforme necessário, e reduza-os quando não forem necessários. Também me permite desligar completamente os processos de trabalho quando necessário e enfileirar os trabalhos até que os trabalhadores voltem a ficar online.
fonte
O PHP é uma linguagem de thread único, portanto, não há maneira oficial de iniciar um processo assíncrono com ele, além de usar
exec
orpopen
. Há um post sobre isso aqui . Sua idéia para uma fila no MySQL também é uma boa idéia.Seu requisito específico aqui é enviar um email ao usuário. Estou curioso para saber por que você está tentando fazer isso de forma assíncrona, pois o envio de um email é uma tarefa bastante trivial e rápida de executar. Suponho que, se você está enviando toneladas de e-mail e seu ISP está bloqueando você por suspeita de spam, esse pode ser um motivo para fazer fila, mas, além disso, não consigo pensar em nenhum motivo para fazê-lo dessa maneira.
fonte