php nginx long-polling

Nginx+PHP: detener el proceso en la solicitud cancelada



long-polling (2)

¿Qué estás haciendo exactamente en esas solicitudes de larga duración? Si lo que está haciendo está causando que el proceso FastCGI espere alguna llamada del sistema, como esperar a que la base de datos devuelva un resultado, la conexión del cliente HTTP abortada no causará la interrupción de esta llamada. Si recuerdo correctamente, el efecto de ignore_user_abort(false) es simplemente que el script PHP se aborta tan pronto como intenta generar algo a la conexión (ahora perdida). La secuencia de comandos no escribirá ningún resultado mientras está esperando una llamada del sistema.

Si es posible, debe dividir la tarea que el script de larga ejecución está realizando en trozos más pequeños y verificar el estado de la conexión entre su procesamiento. Asegúrese de que la secuencia de comandos termina si la conexión se terminó:

while (!$done_yet) { if(connection_status() != CONNECTION_NORMAL) { break; } do_more_work(); }

En la documentación de PHP encontrará más información sobre el manejo de la conexión, si lo desea.

Tengo Nginx 1.4.4 y PHP 5.5.6. Estoy haciendo solicitudes de encuestas largas. El problema es que si cancelo la solicitud HTTP enviada a través de Ajax, las solicitudes aún se están procesando (no se detienen). Lo probé con la función PHP mail () al final del archivo, y el correo sigue llegando, el archivo no se detuvo).

Estoy preocupado, porque creo que podría causar un bloqueo del servidor debido a la alta carga de solicitudes no cerradas. Sí, probé ignore_user_abort(false); pero sin cambios. ¿Es posible que cambie algo en Nginx?

location ~ /.php$ { try_files $uri =404; include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }


La mala noticia es que es casi seguro que no podrá resolver su problema cómo desea resolverlo. La señal FastCGI enviada cuando un cliente cierra una conexión antes de recibir una solicitud es FCGI_ABORT_REQUEST

Un servidor web anula una solicitud FastCGI cuando un cliente HTTP cierra su conexión de transporte mientras la solicitud FastCGI se ejecuta en nombre de ese cliente. La situación puede parecer poco probable; la mayoría de las solicitudes FastCGI tendrán tiempos de respuesta cortos, y el servidor web proporcionará un búfer de salida si el cliente es lento. Pero la aplicación FastCGI puede retrasarse en la comunicación con otro sistema o en la ejecución de un servidor.

Lamentablemente, parece que ni la implementación original de fast-cgi ni PHP-FPM compatibles con la señal FCGI_ABORT_REQUEST, por lo que no pueden interrumpirse.

La buena noticia es que hay mejores maneras de resolver este problema. Básicamente, nunca debe tener solicitudes que demoren mucho en procesarse. En cambio, si una solicitud necesita mucho tiempo para procesarla, debe:

  • Empújelo a una cola de tareas que necesitan ser procesadas.
  • Devuelva el ''ID de tarea'' al cliente.
  • Haga que el cliente realice una encuesta periódicamente para ver si esa "tarea" se ha completado y cuando se complete, muestre los resultados.

Además de esas 3 cosas básicas, si le preocupa perder recursos del sistema cuando un cliente ya no está interesado en los resultados de una solicitud, debe agregar:

  • Divida las tareas en pequeños trabajos, y solo mueva las tareas de un "estado" de trabajo al siguiente, si el cliente todavía está pidiendo el resultado.

No dices cuál es tu tarea de larga duración: simulemos que es para descargar un archivo de imagen grande de otro servidor, manipular esa imagen y luego almacenarla en S3. Entonces los estados para esta tarea serían algo como:

TASK_STATE_QUEUED TASK_STATE_DOWNLOADING //Moves to next state when finished download TASK_STATE_DOWNLOADED TASK_STATE_PROCESSING //Moves to next state when processing finished TASK_STATE_PROCESSED TASK_STATE_UPLOADING_TO_S3 //Moves to next state when uploaded TASK_STATE_FINISHED

Entonces, cuando el cliente envía la solicitud inicial, recupera un ID de tarea y luego cuando consulta el estado de esa tarea, ya sea:

  • El servidor informa que la tarea aún se está trabajando

o

  • Si está en uno de los siguientes estados, la solicitud del cliente pasa al siguiente estado.

es decir

TASK_STATE_QUEUED => TASK_STATE_DOWNLOADING TASK_STATE_DOWNLOADED => TASK_STATE_PROCESSING TASK_STATE_PROCESSED => TASK_STATE_UPLOADING_TO_S3

Así que solo se solicita que el cliente esté interesado en continuar siendo procesado.

Por cierto, recomiendo usar algo que esté diseñado para funcionar de manera Rabbitmq como una cola para mantener la cola de tareas (por ejemplo, Rabbitmq , Redis o Gearman ) en lugar de simplemente usar MySQL o cualquier base de datos. Básicamente, el SQL no es tan bueno para actuar como una cola y sería mejor usar la tecnología apropiada desde el principio, en lugar de usar la tecnología incorrecta para comenzar, y luego tener que cambiarla en una emergencia cuando su base de datos se convierta en una base de datos. sobrecargado cuando se trata de hacer cientos de inserciones, actualizaciones por segundo solo para administrar las tareas.

Como beneficio adicional, al dividir el proceso de ejecución prolongado en tareas, resulta realmente fácil:

  1. Vea dónde se está gastando el tiempo de procesamiento.
  2. Vea y detecte las fluctuaciones en el tiempo de procesamiento (por ejemplo, si CPUS alcanza el 100% de utilización, entonces el cambio de tamaño de la imagen tardará mucho más tiempo).
  3. Tirar más recursos a los pasos que son lentos.
  4. Puede dar mensajes de actualización de estado al cliente, para que puedan ver el progreso en la tarea, lo que le da un mejor UX en lugar de simplemente sentarse allí "sin hacer nada".