¿Enviar solicitud HTTP desde PHP sin esperar respuesta?
curl server-side (8)
Quiero tener una solicitud HTTP GET enviada desde PHP. Ejemplo:
http://tracker.example.com?product_number=5230&price=123.52
La idea es hacer análisis web del lado del servidor: en lugar de enviar información de seguimiento desde JavaScript a un servidor, el servidor envía información de seguimiento directamente a otro servidor.
Requisitos:
La solicitud debe tomar el menor tiempo posible, para no retrasar notablemente el procesamiento de la página PHP.
La respuesta del
tracker.example.com
no necesita ser marcada. Como ejemplos, algunas respuestas posibles detracker.example.com
:200: Está bien, pero no es necesario verificarlo.
404: Mala suerte, pero, de nuevo, no es necesario verificarlo.
301: A pesar de que una redirección sería apropiada, retrasaría el procesamiento de la página PHP, por lo tanto, no lo haga.
En resumen: todas las respuestas pueden descartarse.
Ideas para soluciones:
En una respuesta ahora eliminada, alguien sugirió llamar curl línea de comandos de PHP en un proceso de shell. Esto parece una buena idea, solo que no sé si es conveniente hacer muchos procesos de shell bajo una gran carga.
Encontré php-ga , un paquete para hacer Google Analytics desde el lado del servidor desde PHP. En la página del proyecto, se menciona: "Se puede configurar para [...] usar solicitudes que no sean de bloqueo". Hasta ahora no he encontrado el tiempo para investigar qué método usa php-ga internamente, ¡pero este método podría serlo!
En pocas palabras: ¿Cuál es la mejor solución para hacer seguimiento / análisis genéricos del lado del servidor desde PHP.
En realidad puedes hacer esto usando CURL
directamente.
Los dos lo he implementado usando un tiempo de espera muy corto ( CURLOPT_TIMEOUT_MS
) y / o usando curl_multi_exec
.
Tenga en cuenta: finalmente dejé este método porque no todas las solicitudes se hicieron correctamente. Esto podría haber sido causado por mi propio servidor aunque no he podido descartar la opción de falla de curl.
Implementé la función para solicitar rápidamente GET a la url sin esperar respuesta:
function fast_request($url)
{
$parts=parse_url($url);
$fp = fsockopen($parts[''host''],isset($parts[''port''])?$parts[''port'']:80,$errno, $errstr, 30);
$out = "GET ".$parts[''path'']." HTTP/1.1/r/n";
$out.= "Host: ".$parts[''host'']."/r/n";
$out.= "Content-Length: 0"."/r/n";
$out.= "Connection: Close/r/n/r/n";
fwrite($fp, $out);
fclose($fp);
}
Lamentablemente, PHP, por definición, está bloqueando . Si bien esto es cierto para la mayoría de las funciones y operaciones que normalmente manejará, el escenario actual es diferente.
El proceso que me gusta llamar HTTP-Ping requiere que solo toque un URI específico, lo que obliga al servidor específico a iniciar su lógica interna. Algunas funciones le permiten lograr algo muy similar a este HTTP-ping , al no esperar una respuesta.
Tenga en cuenta que el proceso de hacer ping a una URL es un proceso de dos pasos:
- Resolver el DNS
- Hacer la solicitud
Si bien la solicitud debe ser bastante rápida una vez que se resuelva el DNS y se establezca la conexión, no hay muchas maneras de hacer que el DNS se resuelva más rápido.
Algunas formas de hacer un http-ping son:
- cURL , estableciendo CONNECTION_TIMEOUT en un valor bajo
- fsockopen cerrándose inmediatamente después de escribir
- stream_socket_client (igual que fsockopen) y también agregando
STREAM_CLIENT_ASYNC_CONNECT
Aunque tanto cURL
como fsockopen
bloquean mientras se resuelve el DNS. Me di cuenta de que fsockopen es significativamente más rápido, incluso en el peor de los casos.
stream_socket_client
por otro lado, debería solucionar el problema de resolución de DNS y debería ser la solución óptima en este escenario, pero no he logrado que funcione.
Una solución final es comenzar otro hilo / proceso que lo haga por usted. Hacer una llamada al sistema para esto debería funcionar, pero también el forking el proceso actual debería hacer eso también. Lamentablemente, ninguno de los dos es realmente seguro en aplicaciones donde no se puede controlar el entorno en el que se ejecuta PHP.
Las llamadas al sistema a menudo están bloqueadas y pcntl no está habilitado de manera predeterminada.
Llamaría a tracker.example.com de esta manera:
get_headers(''http://tracker.example.com?product_number=5230&price=123.52'');
y en el script del rastreador:
ob_end_clean();
ignore_user_abort(true);
ob_start();
header("Connection: close");
header("Content-Length: " . ob_get_length());
ob_end_flush();
flush();
// from here the response has been sent. you can now wait as long as you want and do some tracking stuff
sleep(5); //wait 5 seconds
do_some_stuff();
exit;
Necesitaba hacer algo similar, solo hacer ping a una URL y descartar todas las respuestas. Usé el comando proc_open que le permite finalizar el proceso de inmediato usando proc_close. Asumo que tienes lince instalado en tu servidor:
<?php
function ping($url) {
$proc = proc_open("lynx $url",[],$pipes);
proc_close($proc);
}
?>
Puede usar shell_exec y curl de línea de comando.
Para un ejemplo, vea esta pregunta
Vine aquí mientras investigaba un problema similar. Si tiene una conexión de base de datos a mano, otra posibilidad es rellenar rápidamente los detalles de la solicitud en una tabla y luego tener un proceso independiente basado en cron que escanea periódicamente esa tabla para que se procesen nuevos registros y hace la solicitud de seguimiento, liberando su aplicación web debe tener que realizar la solicitud HTTP por sí misma.
<?php
// Create a stream
$opts = array(
''http''=>array(
''method''=>"GET",
''header''=>"Accept-language: en"
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents(''http://tracker.example.com?product_number=5230&price=123.52'', false, $context);
?>