Estou tentando fazer uma chamada AJAX (via JQuery) que iniciará um processo bastante longo. Gostaria que o script simplesmente enviasse uma resposta indicando que o processo foi iniciado, mas o JQuery não retornará a resposta até que o script PHP termine de ser executado.
Eu tentei isso com um cabeçalho "close" (abaixo) e também com buffer de saída; nem parece funcionar. Qualquer suposição? ou é algo que preciso fazer em JQuery?
<?php
echo( "We'll email you as soon as this is done." );
header( "Connection: Close" );
// do some stuff that will take a while
mail( '[email protected]', "okay I'm done", 'Yup, all done.' );
?>
Respostas:
A seguinte página de manual do PHP (incluindo notas do usuário) sugere várias instruções sobre como fechar a conexão TCP com o navegador sem encerrar o script PHP:
Supostamente, isso requer um pouco mais do que enviar um cabeçalho fechado.
OP então confirma: sim, isso funcionou: apontando para a nota do usuário nº 71172 (novembro de 2006) copiada aqui:
Mais tarde, em julho de 2010, em uma resposta relacionada, o Arctic Fire vinculou duas outras notas do usuário que eram acompanhamentos à anterior:
fonte
Connection: close
cabeçalho pode ser sobrescrito por outro software na pilha, por exemplo, o proxy reverso no caso de um CGI (observei esse comportamento com o nginx). Veja a resposta de @hanshenrik sobre isso. Em geral,Connection: close
é executado no lado do cliente e não deve ser considerado uma resposta a esta pergunta. A conexão deve ser encerrada do lado do servidor .É necessário enviar estes 2 cabeçalhos:
Como você precisa saber o tamanho de sua saída, precisará armazená-la em buffer e, em seguida, descarregá-la no navegador:
Além disso, se o seu servidor web estiver usando compactação gzip automática na saída (ou seja, Apache com mod_deflate), isso não funcionará porque o tamanho real da saída foi alterado e o Content-Length não é mais preciso. Desative a compactação gzip do script específico.
Para obter mais detalhes, visite http://www.zulius.com/how-to/close-browser-connection-continue-execution
fonte
header("Content-Encoding: none\r\n");
Assim o apache não a comprimirá.ob_flush()
não é necessário e realmente causa um avisofailed to flush buffer
. Eu tirei e funcionou muito bem.ob_flush()
linha era necessária.Você pode usar Fast-CGI com PHP-FPM para usar a
fastcgi_end_request()
função . Dessa forma, você pode continuar a fazer algum processamento enquanto a resposta já foi enviada ao cliente.Você encontra isso no manual do PHP aqui: FastCGI Process Manager (FPM) ; Mas essa função especificamente não está mais documentada no manual. Aqui está o trecho do PHP-FPM: Wiki do PHP FastCGI Process Manager :
fastcgi_finish_request ()
Escopo: função php
Categoria: Otimização
Este recurso permite que você acelere a implementação de algumas consultas php. A aceleração é possível quando há ações no processo de execução do script que não afetam a resposta do servidor. Por exemplo, salvar a sessão no memcached pode ocorrer após a página ser formada e passada para um servidor da web.
fastcgi_finish_request()
é um recurso php, que interrompe a saída de resposta. O servidor da Web imediatamente começa a transferir a resposta "lenta e tristemente" para o cliente, e o php ao mesmo tempo pode fazer muitas coisas úteis no contexto de uma consulta, como salvar a sessão, converter o vídeo baixado, lidar com todos os tipos de estatísticas, etc.fastcgi_finish_request()
pode invocar a execução da função de desligamento.Nota:
fastcgi_finish_request()
tem uma peculiaridade onde as chamadas paraflush
,print
ouecho
irá encerrar o script inicial.Para evitar esse problema, você pode ligar
ignore_user_abort(true)
antes ou depois dafastcgi_finish_request
ligação:fonte
Versão completa:
fonte
Uma solução melhor é fazer um fork de um processo em segundo plano. É bastante simples em unix / linux:
Você deve olhar para esta questão para obter melhores exemplos:
PHP executa um processo em segundo plano
fonte
Supondo que você tenha um servidor Linux e acesso root, tente isto. É a solução mais simples que encontrei.
Crie um novo diretório para os arquivos a seguir e conceda a ele permissões totais. (Podemos torná-lo mais seguro mais tarde.)
Coloque isso em um arquivo chamado
bgping
.Observe o
&
. O comando ping será executado em segundo plano enquanto o processo atual passa para o comando echo. Ele fará ping em www.google.com 15 vezes, o que levará cerca de 15 segundos.Torne-o executável.
Coloque isso em um arquivo chamado
bgtest.php
.Ao solicitar bgtest.php em seu navegador, você deve obter a seguinte resposta rapidamente, sem esperar cerca de 15 segundos para que o comando ping seja concluído.
O comando ping agora deve estar em execução no servidor. Em vez do comando ping, você pode executar um script PHP:
Espero que isto ajude!
fonte
Aqui está uma modificação no código do Timbo que funciona com compactação gzip.
fonte
Estou em um host compartilhado e
fastcgi_finish_request
configurado para sair dos scripts completamente. Também não gosto daconnection: close
solução. Seu uso força uma conexão separada para solicitações subsequentes, custando recursos adicionais do servidor. Eu li oTransfer-Encoding: cunked
artigo da Wikipedia e descobri que0\r\n\r\n
uma resposta termina. Eu não testei completamente isso em todas as versões de navegadores e dispositivos, mas funciona em todos os 4 dos meus navegadores atuais.fonte
Você pode tentar fazer multithreading.
você poderia preparar um script que faz uma chamada de sistema (usando shell_exec ) que chama o binário php com o script para fazer seu trabalho como parâmetro. Mas não acho que seja a forma mais segura. Talvez você possa fortalecer as coisas fazendo chroot no processo php e outras coisas
Alternativamente, há uma classe em phpclasses que faz isso http://www.phpclasses.org/browse/package/3953.html . Mas eu não sei os detalhes da implementação
fonte
&
caractere para rodar o processo em segundo plano.Resposta do TL; DR:
Resposta da função:
fonte
Seu problema pode ser resolvido fazendo alguma programação paralela em php. Eu fiz uma pergunta sobre isso algumas semanas atrás aqui: Como alguém pode usar multi-threading em aplicativos PHP
E obteve ótimas respostas. Gostei muito de um em particular. O autor fez uma referência ao tutorial Easy Parallel Processing in PHP (Set 2008; by johnlim) que pode realmente resolver seu problema muito bem, pois já o usei para lidar com um problema semelhante que surgiu alguns dias atrás.
fonte
A resposta de Joeri Sebrechts está fechada, mas destrói qualquer conteúdo existente que possa ser armazenado em buffer antes de você desejar se desconectar. Ele não chama
ignore_user_abort
corretamente, permitindo que o script seja encerrado prematuramente. A resposta do diyism é boa, mas não é genericamente aplicável. Por exemplo, uma pessoa pode ter mais ou menos buffers de saída que aquela resposta não trata, então pode simplesmente não funcionar na sua situação e você não saberá por quê.Esta função permite que você se desconecte a qualquer momento (desde que os cabeçalhos ainda não tenham sido enviados) e retém o conteúdo gerado até agora. O tempo extra de processamento é ilimitado por padrão.
Se você também precisar de memória extra, aloque-a antes de chamar esta função.
fonte
Nota para usuários mod_fcgid (por favor, use por sua própria conta e risco).
Solução Rápida
A resposta aceita de Joeri Sebrechts é de fato funcional. No entanto, se você usar o mod_fcgid, poderá descobrir que esta solução não funciona sozinha. Em outras palavras, quando a função flush é chamada, a conexão com o cliente não é fechada.
O
FcgidOutputBufferSize
parâmetro de configuração de mod_fcgid pode ser o culpado. Encontrei esta dica em:Depois de ler o acima, você pode chegar à conclusão de que uma solução rápida seria adicionar a linha (consulte "Exemplo de Host Virtual" no final):
em seu arquivo de configuração do Apache (por exemplo, httpd.conf), seu arquivo de configuração FCGI (por exemplo, fcgid.conf) ou em seu arquivo de hosts virtuais (por exemplo, httpd-vhosts.conf).
Detalhes e uma segunda solução
A solução acima desativa o buffering executado por mod_fcgid para todo o servidor ou para um host virtual específico. Isso pode levar a uma penalidade de desempenho para o seu site. Por outro lado, pode muito bem não ser o caso, já que o PHP realiza o buffer por conta própria.
Caso você não queira desabilitar o buffer de mod_fcgid , há outra solução ... você pode forçar o esvaziamento deste buffer .
O código abaixo faz exatamente isso com base na solução proposta por Joeri Sebrechts:
O que a linha de código adicionada essencialmente faz é preencher o buffer de mod_fcgi , forçando-o a esvaziar. O número "65537" foi escolhido porque o valor padrão da
FcgidOutputBufferSize
variável é "65536", conforme mencionado na página da web do Apache para a diretiva correspondente . Portanto, pode ser necessário ajustar esse valor adequadamente se outro valor for definido em seu ambiente.Meu ambiente
Exemplo de host virtual
fonte
isso funcionou para mim
fonte
Ok, então basicamente da forma como o jQuery faz a solicitação XHR, até mesmo o método ob_flush não funcionará porque você não consegue executar uma função em cada onreadystatechange. jQuery verifica o estado e, em seguida, escolhe as ações adequadas a serem tomadas (completo, erro, sucesso, tempo limite). E embora eu não tenha conseguido encontrar uma referência, lembro-me de ouvir que isso não funciona com todas as implementações de XHR. Um método que acredito que deva funcionar para você é um cruzamento entre a votação ob_flush e a polling para sempre.
E como os scripts são executados em linha, à medida que os buffers são liberados, você obtém a execução. Para tornar isso útil, altere o console.log para um método de retorno de chamada definido na configuração do script principal para receber dados e agir sobre eles. Espero que isto ajude. Saúde, Morgan.
fonte
Uma solução alternativa é adicionar o trabalho a uma fila e fazer um script cron que verifica se há novos trabalhos e os executa.
Tive que fazer isso recentemente para contornar os limites impostos por um host compartilhado - exec () et al foi desabilitado para PHP executado pelo servidor da web, mas podia ser executado em um script de shell.
fonte
Se a
flush()
função não funcionar. Você deve definir as próximas opções no php.ini como:fonte
Última Solução de Trabalho
fonte
Depois de tentar muitas soluções diferentes deste tópico (depois que nenhuma delas funcionou para mim), encontrei a solução na página oficial do PHP.net:
fonte