type - El búfer de salida de PHP no descarga




php header location); (4)

Tengo una serie de scripts que muestran el progreso mientras se ejecutan porque son de larga ejecución. Básicamente, cada uno hace lo siguiente al final de cada fila en bucle de datos procesados:

echo '.';
@ob_flush();
flush();

Esto funcionó perfectamente durante años y luego actualicé a PHP 5.3.x y Apache 2.2.x en varios servidores. Ahora, incluso si relleno el búfer con espacios en blanco o configuro "ob_implicit_flush (1)", no puedo mostrar el resultado en el comando.

Un servidor todavía muestra la salida, pero está en pedazos. Puede tomar casi 5 minutos y de repente aparece una cadena de puntos en la pantalla. Con los otros servidores, no obtengo nada hasta que el script termina de ejecutarse por completo.

Intenté buscar en los archivos php.ini y httpd.conf para ver si podía averiguar qué había cambiado entre los diferentes servidores, pero obviamente me falta algo.

También intenté deshabilitar mod_deflate en .htaccess para los scripts afectados, pero eso tampoco ayuda (desactivando mod_gzip utilizado para solucionar el problema de inmediato).

¿Puede alguien señalarme en la dirección correcta con esto, por favor? No poder monitorear la ejecución de scripts en tiempo real está causando todo tipo de problemas, pero no podemos seguir usando estas versiones anteriores de PHP.

En una nota al margen aún más peculiar, intenté degradar un servidor a PHP 5.2.17 pero el problema del buffer de salida permaneció después de la degradación. Esto me hace sospechar que se trata de algo relacionado con la forma en que Apache maneja la salida de PHP, ya que Apache 2 se dejó en su lugar.


ob_flush () (an flush ()) solo vacía el búfer PHP - el servidor web mantiene un buffer en sí mismo. Y por extraño que pueda parecer, enjuagar el buffer de forma anticipada en realidad disminuye el rendimiento en el servidor, por lo tanto, las versiones más recientes de apache se almacenan de forma más agresiva. También hay problemas terribles relacionados con la compresión y la representación parcial cuando se trabaja con codificación por fragmentos HTTP.

Si desea agregar contenido gradualmente a una página, utilice ajax o websockets para agregarlo a la vez.


Este problema tiene más que ver con su servidor (apache) que con la versión de php.

Una opción es desactivar el almacenamiento en búfer de salida, aunque el rendimiento puede sufrir en otras partes del sitio

En Apache

Establezca la directiva php ini ( output_buffering=off ) desde la configuración del servidor, incluido un archivo .htaccess. Así que usé lo siguiente en un archivo .htaccess para desactivar el output_buffering solo para ese archivo:

<Files "q.php">
    php_value output_buffering Off
</Files>

Y luego, en la configuración de mi servidor estático, solo necesitaba AllowOverride Options=php_value (o un martillo más grande, como AllowOverride All ) para que eso se permitiera en un archivo .htaccess .

En Nginx

Para deshabilitar el almacenamiento en búfer para Nginx (agregue "proxy_buffering off;" al archivo de configuración y reinicie Nginx


Intenté todo para que esto funcione, incluidas todas las configuraciones conocidas enumeradas anteriormente. He estado tratando de usar PHP para servir archivos de video fragmentados usando HTTP_RANGE y no estaba funcionando.

Después de sacar la mayor parte de mi cabello, encontré la respuesta: tienes que generar al menos un byte más que el tamaño del búfer para que salga al navegador. Aquí está el guión que terminó trabajando para mí:

<?php 
// Close open sessions
session_write_close();

// Turn off apache-level compression
@apache_setenv('no-gzip', 1);

// Turn off compression
@ini_set('zlib.output_compression', 0);

// Turn error reporting off
@ini_set('error_reporting', E_ALL & ~ E_NOTICE);

// Tell browser not to cache this
header("Cache-Control: no-cache, must-revalidate");

// close any existing buffers
while (ob_get_level()) ob_end_clean();

// Set this to whatever you like
$buffer = 8096;

for($i = 1; $i <= 8; $i++) 
{
    // Start a output buffer with specified size
    ob_start(null,$buffer,PHP_OUTPUT_HANDLER_FLUSHABLE);
    // Output exactly one byte more than that size 
    // \n == 2 bytes, so 8096-1+2 = 8097
    echo str_repeat('=', $buffer-1)."\n";
    // 0.25s nap
    usleep(250000);
    // End output buffering and flush it
    ob_end_flush();
    flush();
}

¡Espero que esto ayude a alguien!


Existe una solución posible para este problema que no requiere que edite sus scripts existentes o modifique la configuración de su servidor para detener el almacenamiento en búfer. Mediante el uso de un script de contenedor puede iniciar su proceso de larga ejecución en segundo plano desde el script php que sirve la solicitud web. A continuación, puede canalizar la salida del proceso de ejecución larga en un archivo de texto que se puede leer fácilmente para encontrar el progreso actual del script mediante sondeo. Ejemplo a continuación:

Script de proceso de larga duración

<?php
// long_process.php
echo "I am a long running process ";
for ($i = 0; $i < 10; $i++) {
    echo ".";
    sleep(1);
}
echo " Processing complete";
?>

Secuencia de comandos para inicializar el proceso de larga ejecución y ver la salida

<?php
    // proc_watcher.php
    $output = './output.txt';
    if ($_GET['action'] == 'start') {
        echo 'starting running long process<br>';
        $handle = popen("nohup php ./long_process.php > $output &", 'r');
        pclose($handle);
    } else {
        echo 'Progress at ' . date('H:i:s') . '<br>';
        echo file_get_contents($output);
    }
    $url = 'proc_watcher.php';
?>
<script>
    window.setTimeout(function() {
         window.location = '<?php echo $url;?>';
    }, 1000);
</script>

Si envía una solicitud web a proc_watcher.php?action=start la secuencia de comandos debería iniciar el proceso de larga ejecución en segundo plano y luego devolver el contenido del archivo de salida al navegador web cada segundo.

El truco aquí es la línea de comandos nohup php ./long_process.php > ./output.txt & , que ejecuta el proceso en segundo plano y envía la salida a un archivo en lugar de STDOUT.





apache