2015-04-04 3 views
2

У меня есть небольшой скрипт, который обновляет базу данных. В настоящее время требуется около минуты, но время будет расти по мере роста базы данных. Я хочу предоставлять обновления пользователю, когда скрипт работает, чтобы они знали, что статус скрипта и что он все еще продолжается. Я реализовал Server-Side-Events, и пока он работает, ни одно из обновлений не публикуется на веб-сайте до тех пор, пока скрипт не будет полностью завершен.сервер отправил события, не обновляемые до завершения сценария

Я могу загрузить весь файл сценария, но вот соответствующие части.

Во-первых, в начале скрипта я проверяю, чтобы увидеть, если скрипт запускается из командной строки, и если я не посылать заголовки, как показано ниже:

$cli = (PHP_SAPI === 'cli') ? 1 : 0; 
if(!$cli){ 
    header('Content-Type: text/event-stream'); 
    header('Cache-Control: no-cache'); 
} 

Вот моя функция, чтобы отправить sse_message при вызове

function sse_message($type, $message, $progress=0) 
{ 
    $d = array("type" => $type, "msg" => $message , "progress" => $progress); 
    $id = time(); 

    echo "id: $id" . PHP_EOL; 
    echo "data: " . json_encode($d) . PHP_EOL; 
    echo PHP_EOL; 

    ob_flush(); 
    flush(); 
} 

И, наконец, вот мой Javascript реализация слушателя:

if(typeof(EventSource) !== "undefined"){ 
    source = new EventSource("../priv3/script/prowip_process.php"); 
    source.addEventListener("message", function(e) 
    { 
     var result = JSON.parse(e.data); 

     switch(result.type){ 
      case "update": 
       if(result.msg != "") 
        document.getElementById("prowip_status").value += "\n[INFO]: "+result.msg; 
       document.getElementById("prowip_progress").value = result.progress; 
       break; 

      case "success": 
       document.getElementById("prowip_status").value += "\n[COMPLETED]:"+result.msg; 
       document.getElementById("prowip_progress").value = 100; 
       document.getElementById("button_prowip_exit").disabled = false; 
       source.close(); 
       break; 

      case "error": 
       document.getElementById("prowip_status").value += "\n[ERROR]: "+result.msg; 
       document.getElementById("button_prowip_exit").disabled = false; 
       source.close(); 
       break;  
     } 
    }, false); 
} 
else{ 
    document.getElementById("prowip_status").value += "\n\n[ERROR]: Your browser does not support server-sent events. Please upgrade your browser or use a different browser for this function. Please note this script will not work with Internet Explorer." 
    document.getElementById("button_prowip_exit").disabled = false; 
} 

Снова - все работает отлично, за исключением того, что во время процесса сценария не происходит никаких обновлений, а затем, когда он заканчивает все сообщения и индикатор выполнения, обновляется.

Я пробовал это в нескольких браузерах, компьютерах и т. Д., Но ни одно обновление на веб-странице не происходит до тех пор, пока не будет выполнен весь сценарий.

Любая помощь или комментарии были бы весьма признательны. Благодарю.

EDITED 4/3/2015 К POST больше кода в соответствии с просьбой

Эти 3 функции вызывают sse_message или просто эхо для CLI.

function show_error($cli, $message) 
{ 
    if ($cli) 
     echo $message."\n"; 
    else 
     sse_message("error", $message); 
} 

function show_message($cli, $message, $progress) 
{ 
    if ($cli) 
     echo $message."[".$progress."%]\n"; 
    else 
     sse_message("update", $message, $progress); 
} 

function show_success($cli, $message) 
{ 
    if ($cli) 
     echo $message."[100%]\n"; 
    else 
     sse_message("success", $message); 
} 

Вот скрепка из основной функции, которая показывает, как эти функции называются:

foreach ($projects as $number => $project) { 
      $cn = strtok($number, "."); 
      $pn = strtok("."); 
      $wipover = $project['wip'] - $project['wip30'] - $project['wip60'] - $project['wip90']; 
      $arover = $project['ar'] - $project['ar30'] - $project['ar60'] - $project['ar90']; 
      $process_counter++; 
      if($process_counter >= $check){ 
       $progress+=5; 
       $check += intval ($counter/16); 
       show_message($cli,"",$progress); 
      } 
      if(($project['empty']==1) and ($project['wip']==0) and ($project['ar']==0)){ 
       //Do nothing... 
      } 
      else{ 
       $write_counter++; 
       $update_query = "update projects set wip='{$project['wip']}', wip30='{$project['wip30']}', wip60='{$project['wip60']}', wip90='{$project['wip90']}', wipover='$wipover', ar='{$project['ar']}', ar30='{$project['ar30']}', ar60='{$project['ar60']}', ar90='{$project['ar90']}', arover='$arover' where client_num='$cn' and new_num='$pn'"; 
       if(! $db->query($update_query)){ 
        show_error($cli, "ERROR - update query: [$update_query] returned error: [".$db->error."]", $progress); 
       } 
      } 
     } 
+0

Можете ли вы показать основной цикл (т. Е. Часть скрипта PHP, которая вызывает 'sse_message')? Если вы установите для своей версии CLI также вызов 'sse_message()', сколько раз она будет вызвана во время обработки вашей минуты, когда она запускается из командной строки? –

+0

У меня есть 3 простых функции, которые вызывается в зависимости от типа сообщения, поэтому версия CLI не вызывает sse_message и вместо этого использует экранное эхо. Я отправил эти функции в исходное сообщение.Затем основной цикл вызывает одну из этих трех функций. Я опубликовал клип этого процесса. Я могу опубликовать всю функцию, если это будет полезно. Вероятно, sse_message называется около 30 раз. –

+0

Все кажется разумным, не так ли. Если вы посмотрите в консоли разработчика вашего браузера, вкладке сети, видите ли вы, что полученные байты постепенно увеличиваются. Или вы видите 0 полученных байтов, затем все байты принимаются в конце? Мое предположение - последнее, и кеширование происходит на стороне сервера (или на промежуточном кэшировании или прокси-сервере). Вы используете Apache и обычный php-модуль apache? –

ответ

0

Я была такая же проблема. Оказалось, что веб-сервер кэширует вывод из php.

Внутри PHP ob_flush() и flush() выдает вывод из PHP на веб-сервер, но не может заставить веб-сервер вытолкнуть его клиенту.

Мое решение состояло в том, чтобы вывести результат до 800 000 символов перед тем, как вытереть с PHP. то есть:

echo "data: " . str_pad($message, 800000) . PHP_EOL . PHP_EOL; 

Каждый ответ обратно клиенту приблизительно 6Kb большой, так что это не очень эффективно, но это был единственный способ, которым я мог заставить его работать.

Надеюсь, что это поможет!

Смежные вопросы