Estoy intentando hacer una llamada AJAX (a través de JQuery) que iniciará un proceso bastante largo. Me gustaría que el script simplemente envíe una respuesta indicando que el proceso ha comenzado, pero JQuery no devolverá la respuesta hasta que el script PHP termine de ejecutarse.
He intentado esto con un encabezado "cerrar" (abajo), y también con búfer de salida; ninguno parece funcionar. ¿Alguna conjetura? ¿O es esto algo que necesito hacer en 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.' );
?>
Respuestas:
La siguiente página del manual de PHP (incluidas las notas del usuario) sugiere varias instrucciones sobre cómo cerrar la conexión TCP al navegador sin finalizar el script PHP:
Supuestamente requiere un poco más que enviar un encabezado cerrado.
OP luego confirma: sí , esto hizo el truco: apuntando a la nota de usuario # 71172 (noviembre de 2006) copiada aquí:
Más tarde, en julio de 2010, en una respuesta relacionada, Arctic Fire luego vinculó dos notas de usuario adicionales que eran seguimientos a la anterior:
fuente
Connection: close
encabezado puede ser sobrescrito por el otro software en la pila, por ejemplo, el proxy inverso en el caso de un CGI (observé ese comportamiento con nginx). Vea la respuesta de @hanshenrik sobre eso. En general,Connection: close
se ejecuta en el lado del cliente y no debe considerarse como una respuesta a esta pregunta. La conexión debe cerrarse desde el lado del servidor .Es necesario enviar estos 2 encabezados:
Como necesita saber el tamaño de su salida, deberá almacenar su salida en búfer y luego descargarla en el navegador:
Además, si su servidor web utiliza la compresión gzip automática en la salida (es decir, Apache con mod_deflate), esto no funcionará porque el tamaño real de la salida cambia y la longitud del contenido ya no es precisa. Desactive la compresión gzip del script en particular.
Para obtener más detalles, visite http://www.zulius.com/how-to/close-browser-connection-continue-execution
fuente
header("Content-Encoding: none\r\n");
esa manera, apache no la comprimirá.ob_flush()
es necesario y en realidad provoca un avisofailed to flush buffer
. Lo saqué y funcionó muy bien.ob_flush()
línea era necesaria.Puede usar Fast-CGI con PHP-FPM para usar la
fastcgi_end_request()
función . De esta manera, puede continuar con el procesamiento mientras la respuesta ya ha sido enviada al cliente.Puede encontrar esto en el manual de PHP aquí: FastCGI Process Manager (FPM) ; Pero esa función específicamente no se documenta más en el manual. Aquí el extracto de PHP-FPM: PHP FastCGI Process Manager Wiki :
fastcgi_finish_request ()
Alcance: función php
Categoría: Optimización
Esta característica le permite acelerar la implementación de algunas consultas php. La aceleración es posible cuando hay acciones en el proceso de ejecución del script que no afectan la respuesta del servidor. Por ejemplo, guardar la sesión en Memcached puede ocurrir después de que la página se haya formado y pasado a un servidor web.
fastcgi_finish_request()
es una función de php que detiene la salida de respuesta. El servidor web inmediatamente comienza a transferir la respuesta "lenta y tristemente" al cliente, y php al mismo tiempo puede hacer muchas cosas útiles en el contexto de una consulta, como guardar la sesión, convertir el video descargado, manejar todo tipo de estadísticas, etc.fastcgi_finish_request()
puede invocar la ejecución de la función de apagado.Nota:
fastcgi_finish_request()
tiene una peculiaridad que las llamadas aflush
,print
oecho
dará por terminado el guión antes de tiempo.Para evitar ese problema, puede llamar
ignore_user_abort(true)
justo antes o después de lafastcgi_finish_request
llamada:fuente
Versión completa:
fuente
Una mejor solución es bifurcar un proceso en segundo plano. Es bastante sencillo en unix / linux:
Debería mirar esta pregunta para obtener mejores ejemplos:
PHP ejecuta un proceso en segundo plano
fuente
Suponiendo que tiene un servidor Linux y acceso de root, intente esto. Es la solución más sencilla que he encontrado.
Cree un nuevo directorio para los siguientes archivos y otórguele todos los permisos. (Podemos hacerlo más seguro más adelante).
Pon esto en un archivo llamado
bgping
.Tenga en cuenta el
&
. El comando ping se ejecutará en segundo plano mientras el proceso actual pasa al comando echo. Hará ping a www.google.com 15 veces, lo que tardará unos 15 segundos.Hágalo ejecutable.
Pon esto en un archivo llamado
bgtest.php
.Cuando solicite bgtest.php en su navegador, debería obtener la siguiente respuesta rápidamente, sin esperar unos 15 segundos para que se complete el comando ping.
El comando ping ahora debería estar ejecutándose en el servidor. En lugar del comando ping, puede ejecutar un script PHP:
¡Espero que esto ayude!
fuente
Aquí hay una modificación al código de Timbo que funciona con compresión gzip.
fuente
Estoy en un host compartido y estoy
fastcgi_finish_request
configurado para salir de los scripts por completo. Tampoco me gusta laconnection: close
solución. Su uso fuerza una conexión separada para solicitudes posteriores, lo que cuesta recursos adicionales del servidor. Leí elTransfer-Encoding: cunked
artículo de Wikipedia y supe que0\r\n\r\n
termina una respuesta. No lo he probado a fondo en todas las versiones de navegadores y dispositivos, pero funciona en los 4 de mis navegadores actuales.fuente
Podría intentar hacer varios subprocesos.
podría crear un script que haga una llamada al sistema (usando shell_exec ) que llame al binario php con el script para hacer su trabajo como parámetro. Pero no creo que esa sea la forma más segura. Tal vez puedas mejorar las cosas haciendo chroot en el proceso php y otras cosas
Alternativamente, hay una clase en phpclasses que hace eso http://www.phpclasses.org/browse/package/3953.html . Pero no conozco los detalles de la implementación.
fuente
&
personaje para ejecutar el proceso en segundo plano.TL; DR Respuesta:
Respuesta de función:
fuente
Su problema puede resolverse realizando una programación paralela en php. Hice una pregunta al respecto hace unas semanas aquí: ¿Cómo se pueden usar subprocesos múltiples en aplicaciones PHP?
Y obtuve excelentes respuestas. Me gustó mucho uno en particular. El escritor hizo una referencia al tutorial Easy Parallel Processing en PHP (septiembre de 2008; por johnlim) que realmente puede resolver su problema muy bien, ya que ya lo he usado para tratar un problema similar que surgió hace un par de días.
fuente
La respuesta de Joeri Sebrechts es cercana, pero destruye cualquier contenido existente que pueda almacenarse en búfer antes de que desee desconectarse. No llama
ignore_user_abort
correctamente, lo que permite que el script finalice prematuramente. La respuesta del diyismo es buena pero no es aplicable genéricamente. Por ejemplo, una persona puede tener más o menos búferes de salida que esa respuesta no maneja, por lo que es posible que simplemente no funcione en su situación y no sepa por qué.Esta función le permite desconectarse en cualquier momento (siempre que los encabezados no se hayan enviado todavía) y retiene el contenido que ha generado hasta el momento. El tiempo de procesamiento adicional es ilimitado por defecto.
Si también necesita memoria adicional, asígnela antes de llamar a esta función.
fuente
Nota para los usuarios de mod_fcgid (utilice bajo su propio riesgo).
Solución rápida
La respuesta aceptada de Joeri Sebrechts es de hecho funcional. Sin embargo, si usa mod_fcgid, es posible que esta solución no funcione por sí sola. En otras palabras, cuando se llama a la función de descarga , la conexión con el cliente no se cierra.
El
FcgidOutputBufferSize
parámetro de configuración de mod_fcgid puede ser el culpable. He encontrado este consejo en:Después de leer lo anterior, puede llegar a la conclusión de que una solución rápida sería agregar la línea (consulte "Ejemplo de host virtual" al final):
en su archivo de configuración de Apache (por ejemplo, httpd.conf), su archivo de configuración de FCGI (por ejemplo, fcgid.conf) o en su archivo de hosts virtuales (por ejemplo, httpd-vhosts.conf).
Detalles y una segunda solución
La solución anterior deshabilita el almacenamiento en búfer realizado por mod_fcgid para todo el servidor o para un host virtual específico. Esto puede provocar una penalización del rendimiento de su sitio web. Por otro lado, este puede no ser el caso, ya que PHP realiza el almacenamiento en búfer por sí solo.
En caso de que no desee deshabilitar el almacenamiento en búfer de mod_fcgid , existe otra solución ... puede forzar el vaciado de este búfer .
El siguiente código hace precisamente eso basándose en la solución propuesta por Joeri Sebrechts:
Lo que la línea de código agregada esencialmente hace es llenar el búfer de mod_fcgi , forzándolo así a vaciarse. Se eligió el número "65537" porque el valor predeterminado de la
FcgidOutputBufferSize
variable es "65536", como se menciona en la página web de Apache para la directiva correspondiente . Por lo tanto, es posible que deba ajustar este valor en consecuencia si se establece otro valor en su entorno.Mi entorno
Ejemplo de host virtual
fuente
esto funcionó para mí
fuente
Ok, básicamente de la forma en que jQuery hace la solicitud XHR, incluso el método ob_flush no funcionará porque no puede ejecutar una función en cada cambio de estado onready. jQuery verifica el estado y luego elige las acciones adecuadas a tomar (completa, error, éxito, tiempo de espera). Y aunque no pude encontrar una referencia, recuerdo haber escuchado que esto no funciona con todas las implementaciones de XHR. Un método que creo que debería funcionar para usted es un cruce entre el sondeo ob_flush y forever-frame.
Y debido a que los scripts se ejecutan en línea, a medida que se vacían los búferes, se obtiene la ejecución. Para que esto sea útil, cambie el archivo console.log a un método de devolución de llamada definido en la configuración de su script principal para recibir datos y actuar sobre ellos. Espero que esto ayude. Salud, Morgan.
fuente
Una solución alternativa es agregar el trabajo a una cola y crear un script cron que verifique si hay nuevos trabajos y los ejecutará.
Tuve que hacerlo de esa manera recientemente para eludir los límites impuestos por un host compartido: exec () et al estaba deshabilitado para PHP ejecutado por el servidor web, pero podía ejecutarse en un script de shell.
fuente
Si la
flush()
función no funciona. Debe configurar las siguientes opciones en php.ini como:fuente
Última solución de trabajo
fuente
Después de probar muchas soluciones diferentes de este hilo (después de que ninguna de ellas funcionó para mí), encontré una solución en la página oficial de PHP.net:
fuente