Эффективный и удобный способ представления медленно загружающихся результатов.

Я прочитал много подобных вопросов об отмене запроса POST с помощью jQuery, но ни один из них не кажется близким к моему.

У меня есть ваша повседневная форма с PHP-страницей в качестве действия:

<form action="results.php">
  <input name="my-input" type="text">
  <input type="submit" value="submit">
</form>

Обработка results.php на стороне сервера на основе информации о сообщении, указанной в форме, занимает много времени (30 секунд или даже больше, и мы ожидаем увеличения, потому что наше пространство для поиска также увеличится в ближайшие недели). Мы обращаемся к серверу Basex (версия 7.9, без возможности обновления), который содержит все данные. Сгенерированный пользователем код XPath отправляется в форму, а затем URL-адрес действия отправляет код XPath на сервер Basex, который возвращает результаты. С точки зрения удобства использования я уже показываю экран загрузки, чтобы пользователи, по крайней мере, знали, что результаты генерируются:

$("form").submit(function() {
  $("#overlay").show();
});

<div id="overlay"><p>Results are being generated</p></div>

Однако я также хотел бы предоставить пользователям возможность нажать кнопку для отмены запроса и отменить запрос, когда пользователь закрывает страницу. Обратите внимание, что в первом случае (при нажатии кнопки) это также означает, что пользователь должен оставаться на той же странице, может редактировать свой ввод и немедленно повторно отправлять свой запрос. Крайне важно, чтобы при отмене запроса они также могли немедленно отправить его повторно: сервер должен на самом деле прервать и не завершить запрос, прежде чем он сможет обработать новый запрос.

Я придумал что-то вроде этого:

$("form").submit(function() {
  $("#overlay").show();
});
$("#overlay button").click(abortRequest);
$(window).unload(abortRequest);

function abortRequest() {
  // abort correct request
}

<div id="overlay">
  <p>Results are being generated</p>
  <button>Cancel</button>
</div>

Но, как видите, я не совсем уверен, как заполнить abortRequest, чтобы убедиться, что запрос на публикацию прерван, и завершен, чтобы можно было отправить новый запрос. Пожалуйста, заполните пропуски! Или мне нужно .preventDefault() отправить форму и вместо этого сделать вызов ajax() из jQuery?


Как я уже сказал, я также хочу остановить процесс на стороне сервера, и из того, что я прочитал, мне нужно exit() для этого. Но как я могу exit использовать другую функцию PHP? Например, предположим, что в results.php у меня есть сценарий обработки, и мне нужно выйти из этого сценария, должен ли я сделать что-то подобное?

<?php
  if (isset($_POST['my-input'])) {
    $input = $_POST['my-input'];
    function processData() {
      // A lot of processing
    }
    processData()
  }

  if (isset($_POST['terminate'])) {
    function terminateProcess() {
      // exit processData()
    }
  }

а затем сделать новый запрос ajax, когда мне нужно завершить процесс?

$("#overlay button").click(abortRequest);
$(window).unload(abortRequest);

function abortRequest() {
  $.ajax({
    url: 'results.php',
    data: {terminate: true},
    type: 'post',
    success: function() {alert("terminated");});
  });
}

Я провел еще несколько исследований и нашел этот ответ. В нем упоминается connection_aborted(), а также session_write_close(), и я не совсем уверен, что мне полезно. Я использую переменные SESSION, но мне не нужно записывать значения при отмене процесса (хотя я хотел бы, чтобы переменные SESSION оставались активными).

Будет ли это путь? И если да, то как заставить одну функцию PHP завершить другую?


Я также читал о веб-сокетах, и мне кажется, что это может работать, но мне не нравятся проблемы с настройкой сервера веб-сокетов, поскольку для этого мне потребуется связаться с нашим ИТ-специалистом, которому требуется тщательное тестирование новых пакетов. Я бы предпочел использовать PHP и JS без сторонних библиотек, кроме jQuery.

Учитывая, что большинство комментариев и ответов предполагают, что то, что я хочу, невозможно, мне также интересно услышать альтернативы. Первое, что приходит на ум, это постраничные вызовы Ajax (аналогично многим веб-страницам, которые предоставляют результаты поиска, изображения и все, что у вас есть, в бесконечной прокрутке). Пользователю предоставляется страница с первыми результатами X (например, 20), и когда они нажимают кнопку «показать следующие 20 результатов», они добавляются. Этот процесс может продолжаться до тех пор, пока не будут показаны все результаты. Поскольку пользователям полезно получать все результаты, я также предоставлю опцию «загрузить все результаты». Это также займет очень много времени, но, по крайней мере, пользователи должны иметь возможность просмотреть первые результаты на самой странице. (Таким образом, кнопка загрузки не должна прерывать постраничную загрузку Ajax.) Это всего лишь идея, но я надеюсь, что она вдохновит некоторых из вас.


person Bram Vanroy    schedule 17.04.2016    source источник
comment
Насколько мне известно, невозможно отменить запрос после того, как сервер начнет его обрабатывать (хотя я бы с радостью ошибся!). Я могу придумать несколько нестандартных решений, но они не стоят 15-секундного процесса. Вы можете попробовать посмотреть веб-сокеты, а не ajax.   -  person jxmallett    schedule 19.04.2016
comment
Да, веб-сокеты могут быть хорошим решением.   -  person Himel Nag Rana    schedule 19.04.2016
comment
Это происходит автоматически. PHP убивает код, как только пользователь отменяет его. Но если нет, вы можете прочитать stackoverflow.com/a/16592945/2729937   -  person Ismael Miguel    schedule 19.04.2016
comment
Что за плохо написанный бэкенд обрабатывается 30 секунд? Вы пытаетесь решить фронтенд, но у вас есть проблема с бэкендом. Легко и просто. Тем не менее, вам нужно промежуточное программное обеспечение: вы отправляете запрос, ему присваивается идентификатор и вся полезная информация (на ум приходит IP-адрес, сгенерированный на лету файл cookie, все, что помогает получить источник запроса), и только потом запрос запущен.   -  person Christian Bonato    schedule 21.04.2016
comment
@BramVanroy — Извините, это прозвучало немного грубо. Тем не менее, разве нельзя что-то сделать с точки зрения предварительной обработки? Какие-то способы отправить эти 50 миллионов токенов? Я предполагаю, что будет быстрее запрашивать 4 или 5 BaseX одновременно, а не только один. Наверняка должна быть какая-то управляемая иерархия из 50 миллионов записей...   -  person Christian Bonato    schedule 21.04.2016
comment
показать пользователю фальшивую загрузку...   -  person itzmukeshy7    schedule 21.04.2016
comment
Кристиан Бонато прав, 30 секунд плохо пахнут, тем более, что вы ожидаете, что они вырастут. В коммерческой публичной среде такая задержка была бы неприемлемой. Вам нужно будет подумать о сегментировании вашей базы данных или вообще изменить механизм базы данных (XML определенно не лучшее решение для такого большого объема данных). Теперь я понимаю, что это исследовательский проект CS, поэтому ожидания не совпадают.   -  person RandomSeed    schedule 24.04.2016
comment
Мы знаем, что XML @RandomSeed тяжеловесен. Однако в лингвистических данных корпуса почти всегда доставляются в виде XML с определенными тегами и атрибутами для слов и частей слов. Преобразование всех этих файлов в другой формат заняло бы слишком много времени (что бы вы предложили?). BaseX — отличная база данных XML, и мне еще предстоит прочитать о более многообещающем XML-сервере. Предложения приветствуются, но мы потратили на это довольно много времени и - хотя мы сами не программисты, по крайней мере, не по образованию - не пришли к лучшей альтернативе. (Читайте о наших усилиях в статье выше.)   -  person Bram Vanroy    schedule 25.04.2016
comment
Боюсь, вы попали в обычную ловушку. XML — отличный формат для обмена данными, а не для хранения данных и уж тем более для запросов данных. Конечно, XPath на удивление эффективен при работе с небольшими и средними наборами данных, но для такого промышленного объема данных вы можете подумать о том, чтобы сначала преобразовать свои данные в обычный механизм базы данных (подойдет любая традиционная СУБД). Мне нравится ваш проект, я был бы заинтересован в том, чтобы внести свой вклад, если вы набираете сотрудников;)   -  person RandomSeed    schedule 26.04.2016
comment
Другим менее драматичным вариантом может быть разделение ваших данных на несколько файлов меньшего размера. Запросы к этим файлам могут выполняться параллельно на разных физических серверах или даже на одной машине (чтобы использовать несколько процессоров). Это называется сегментированием, и похоже, что BaseX не поддерживает его изначально, это следует делать вручную. Мех.   -  person RandomSeed    schedule 26.04.2016


Ответы (6)


Химел Наг Рана продемонстрировал, как отменить ожидающий запрос Ajax. Несколько факторов могут помешать и задержать последующие запросы, как я обсуждал ранее в другом посте.

TL;DR: 1. очень неудобно пытаться определить, что запрос был отменен из самой долго выполняющейся задачи, и 2. в качестве обходного пути вы должны закрыть сеанс (session_write_close()) как можно раньше в вашей длительной задаче, чтобы не блокировать последующие запросы.

connection_aborted() нельзя использовать. Предполагается, что эта функция будет вызываться периодически во время выполнения длинной задачи (обычно внутри цикла). К сожалению, в вашем случае есть только одна важная атомарная операция: запрос к серверной части данных.

Если вы применили процедуры, рекомендованные Химелом Наг Рана и мной, теперь вы сможете отменить запрос Ajax и немедленно разрешить выполнение новых запросов. Единственная проблема, которая остается, заключается в том, что предыдущий (отмененный) запрос может некоторое время продолжать работать в фоновом режиме (не блокируя пользователя, просто тратя ресурсы на сервере).

Проблема может быть перефразирована как «как прервать конкретный процесс извне».

Как справедливо советовал Christian Bonato, вот возможная реализация. Для демонстрации я буду полагаться на компонент Symphony Process, но вы можете разработать более простое индивидуальное решение, если хотите.

Основной подход:

  1. Создайте новый процесс для выполнения запроса, сохраните PID в сеансе. Дождитесь его завершения, затем верните результат клиенту.

  2. Если клиент прерывается, он сигнализирует серверу, что нужно просто убить процесс.


<?php // query.php

use Symfony\Component\Process\PhpProcess;

session_start();

if(isset($_SESSION['queryPID'])) {
    // A query is already running for this session
    // As this should never happen, you may want to raise an error instead
    // of just silently  killing the previous query.
    posix_kill($_SESSION['queryPID'], SIGKILL);
    unset($_SESSION['queryPID']);
}

$queryString = parseRequest($_POST);

$process = new PhpProcess(sprintf(
    '<?php $result = runQuery(%s); echo fetchResult($result);',
    $queryString
));
$process->start();

$_SESSION['queryPID'] = $process->getPid();
session_write_close();
$process->wait();

$result = $process->getOutput();
echo formatResponse($result);

?>


<?php // abort.php

session_start();

if(isset($_SESSION['queryPID'])) {

    $pid = $_SESSION['queryPID'];
    posix_kill($pid, SIGKILL);
    unset($pid);
    echo "Query $pid has been aborted";

} else {

    // there is nothing to abort, send a HTTP error code
    header($_SERVER['SERVER_PROTOCOL'] . ' 599 No pending query', true, 599);

}

?>


// javascript
function abortRequest(pendingXHRRequest) {
    pendingXHRRequest.abort();
    $.ajax({
        url: 'abort.php',
        success: function() { alert("terminated"); });
      });
}

Создать процесс и отслеживать его действительно сложно, поэтому я посоветовал использовать существующие модули. Интеграция только одного компонента Symfony должна быть относительно через Composer: сначала установите Composer, затем компонент Process (composer require symfony/process).

Ручная реализация может выглядеть так (остерегайтесь, это не проверено, неполно и, возможно, нестабильно, но я надеюсь, что вы поняли идею):

<?php // query.php

    session_start();

    $queryString = parseRequest($_POST); // $queryString should be escaped via escapeshellarg()

    $processHandler = popen("/path/to/php-cli/php asyncQuery.php $queryString", 'r');

    // fetch the first line of output, PID expected
    $pid = fgets($processHandler);
    $_SESSION['queryPID'] = $pid;
    session_write_close();

    // fetch the rest of the output
    while($line = fgets($processHandler)) {
        echo $line; // or save this line for further processing, e.g. through json_encode()
    }
    fclose($processHandler);

?>


<?php // asyncQuery.php

    // echo the current PID
    echo getmypid() . PHP_EOL;

    // then execute the query and echo the result
    $result = runQuery($argv[1]);
    echo fetchResult($result);

?>
person RandomSeed    schedule 24.04.2016
comment
Меня немного смущает первый блок кода, особенно PhpProcess (как уже было сказано, я не PHP-разработчик). Я прав, предполагая, что мне это не нужно (так как это от Symfony), только PID? Кроме того, кажется, что магия заключается в posix_kill, но как я могу использовать идентификатор процесса, если я не использую Symfony? Я читал о getmypid, но это не кажется надежным. - person Bram Vanroy; 24.04.2016
comment
Я принял ваш ответ, так как срок действия награды истекает, и ваш ответ кажется наиболее обнадеживающим. Тем не менее, я хотел бы продолжить ответ на вопрос о PhpProcess и идентификаторе процесса выше. Спасибо. - person Bram Vanroy; 25.04.2016
comment
@BramVanroy Извините, я был занят в последнее время. Смотрите мое редактирование, я надеюсь, что это поможет. Если вы беспокоитесь о том, что PID не уникален, обычно это последовательное целое число до 32767. Если ваш процесс длится достаточно долго, чтобы система порождала больше процессов, чем это количество, то пришло время перейти к серьезному решению ( поиск очереди сообщений). - person RandomSeed; 26.04.2016

В моем понимании ключевые моменты:

  • Вы не можете отменить конкретный запрос, если форма отправлена. Причина заключается в том, что на стороне клиента у вас нет ничего, чтобы вы могли определить состояния запроса формы (если он опубликован, если он обрабатывается и т. д.). Поэтому единственный способ отменить это — сбросить $_POST переменные и/или обновить страницу. Таким образом, соединение будет разорвано, и предыдущий запрос не будет выполнен.

  • В вашем альтернативном решении, когда вы отправляете другой вызов Ajax с помощью {terminate: true}, result.php может остановить обработку с помощью простого die(). Но так как это будет вызов async, вы не можете сопоставить его с предыдущим вызовом form submit. Так что это практически не сработает.

  • Вероятное решение: отправить форму с помощью Ajax. С jQuery ajax у вас будет xhr объект, который вы сможете abort() выгрузить из окна.

ОБНОВЛЕНИЕ (после комментария):

  • Синхронный запрос — это когда ваша страница блокирует (все действия пользователя) до тех пор, пока результат не будет готов. Нажатие кнопки отправки в форме — синхронный вызов сервера путем отправки формы — по определению [https://www.w3.org/TR/html-markup/button.submit.html].

  • Теперь, когда пользователь нажимает кнопку отправки, соединение браузера с сервером является синхронным, поэтому оно не будет затруднено до тех пор, пока не будет получен результат. Поэтому, когда выполняются другие вызовы на сервер - во время процесса отправки - ссылка на эту операцию недоступна для других - поскольку она не завершена. Это причина, по которой отправка терминирующего вызова с помощью Ajax не будет работать.

  • В-третьих: для вашего случая вы можете рассмотреть следующий пример кода:

HTML:

<form action="results.php">
  <input name="my-input" type="text">
  <input id="resultMaker" type="button" value="submit">
</form>

<div id="overlay">
  <p>Results are being generated</p>
  <button>Cancel</button>
</div>

JQUERY:

<script type="text/javascript">
    var jqXhr = '';

    $('#resultMaker').on('click', function(){

      $("#overlay").show();

      jqXhr = $.ajax({
        url: 'results.php',
        data: $('form').serialize(),
        type: 'post',
        success: function() {
           $("#overlay").hide();
        });
      });
    });

    var abortRequest = function(){
      if (jqXhr != '') {
        jqXhr.abort();
      }
    };

    $("#overlay button").on('click', abortRequest);
    window.addEventListener('unload', abortRequest);
</script>

Это пример кода - я просто использовал ваши примеры кода и кое-что изменил здесь и там.

person Himel Nag Rana    schedule 19.04.2016
comment
:-) хорошо, позвольте мне немного расширить свой ответ. - person Himel Nag Rana; 19.04.2016
comment
Не уверен, почему вы смешиваете vanilla JS и jQuery. Также вы отправляете данные только по URL-адресу, но не перенаправляете после отправки. - person Bram Vanroy; 20.04.2016
comment
@BramVanroy, Во-первых, это ванильная часть JS - селектор jQuery unload устарел после версии 1.8. Поэтому более безопасный способ - использовать традиционный способ в этом случае. Во-вторых, вы можете выполнить перенаправление в функции success с помощью window.location.href=<your url> -- и я думал, что перенаправление не является проблемой - как я упоминал в своем замечании - перенаправление означает синхронное соединение, которое нельзя прервать. - person Himel Nag Rana; 20.04.2016
comment
Я бы добавил к этому ответу, что вам также нужно будет постоянно проверять, что возвращает connection_aborted(). Вы должны добавить такие вызовы в некоторые контрольные точки в вашем коде, и когда он вернет 1, вам нужно отменить обработку. Кроме того, если вы запускаете какой-то медленный внешний код, вам может потребоваться ввести файл блокировки, чтобы не запускать новую обработку, пока текущая не прервется. - person Ivan Yarych; 20.04.2016
comment
Спасибо за ответ. Я вижу вашу точку зрения. Я попробовал это, и это работает, но, очевидно, я все еще не могу остановить сервер. Поэтому, пожалуйста, смотрите мое редактирование. - person Bram Vanroy; 21.04.2016
comment
Привет @BramVanroy, прочитай правку. Что ж, мой комментарий будет заключаться в том, что без обширной индивидуальной работы над каждым вызовом со стороны клиента на сервер не требуется останавливать сервер для обработки. Однако вы можете прочитать это (symcbean.blogspot. com/2010/02/) и это (reviewsignal.com/blog/2013/08/22/long-running-processes-in-php). Спасибо. :-) - person Himel Nag Rana; 22.04.2016

В BaseX 8.4 появилась новая аннотация RESTXQ %rest:single, которая позволяет отменить выполняющийся запрос на стороне сервера: http://docs.basex.org/wiki/RESTXQ#Query_Execution. Он должен решить хотя бы некоторые из описанных вами проблем.

Текущий способ вернуть только фрагменты результата — передать индекс первому и последнему результату в вашем результате и выполнить фильтрацию в XQuery:

$results[position() = $start to $end]

Возвращая на один результат больше, чем запрошено, клиент будет знать, что результатов будет больше. Это может быть полезно, потому что вычисление общего размера результата часто намного дороже, чем возврат только первых результатов.

person Christian Grün    schedule 21.04.2016
comment
Привет, Кристиан, спасибо за быстрый ответ! К сожалению, мы застряли на 7.9. Я добавил эту информацию в свой первоначальный пост. - person Bram Vanroy; 21.04.2016
comment
Извините, я вижу. Это действительно требует некоторых дополнительных размышлений. – Я только что расширил свой первоначальный ответ, чтобы дать еще несколько отзывов о фрагментации результата. - person Christian Grün; 21.04.2016
comment
В ответ на ваше дополнение: у меня есть ваш ответ на этот вопрос. Итак, если бы я использовал position() = 11 to 20, это показало бы только десять следующих результатов? Так что теоретически я мог бы увеличить позицию через PHP, как я полагаю. Но дает ли это прирост скорости? Если я нажму кнопку (и попрошу следующий X) результатов, а позиция будет от 20 до 30, будет ли XQuery также искать/обнаруживать hts 1-19? Или Basex достаточно умен, чтобы понять, где он остановился? - person Bram Vanroy; 21.04.2016
comment
Точно. Это будет намного, намного быстрее. Если ваш запрос позволяет итеративно обрабатывать результаты. Это напр. случай для (//*[text() = 'bla'])[position() = от 1 до 10], но это невозможно, если у вас есть блокирующие операторы, такие как, например, order by. В последнем случае необходимо проверить все результаты, чтобы определить, какой из них необходимо вывести. – Если вы запрашиваете совпадения 20–30, ваш запрос также будет получать результаты 1–19, но обычно это едва заметно по сравнению с получением всего набора результатов. - person Christian Grün; 22.04.2016
comment
Так что, если я правильно понимаю, это происходит так. Загрузка страницы, показать 20 первых результатов. Пользователь нажимает кнопку (установлена ​​позиция 21-40). Basex находит результаты 0-40, но возвращает только 21-40. Пользователь снова нажимает кнопку (установлено положение 41-60). Basex находит 0-60, но возвращает только 41-60. И так далее. Таким образом, каждый раз basex должен выполнять новый запрос, и чем больше раз пользователь нажимает кнопку, тем больше времени это займет (потому что в конечном итоге они будут искать, скажем, 1021-1040, что означает поиск 0-1040). . Но с другой стороны, это более удобно для пользователя, и пользователь, вероятно, все равно не будет просматривать ВСЕ результаты. - person Bram Vanroy; 22.04.2016
comment
(комментарий был слишком длинным), и я предполагаю, что из-за того, что нажатия кнопок не сильно отличаются друг от друга во времени, должны быть и некоторые преимущества кэширования? Или кеш очищается при закрытии сеанса? - person Bram Vanroy; 22.04.2016
comment
Результаты не будут кэшироваться по умолчанию (поскольку ваши данные всегда могут измениться в фоновом режиме, пока вы запрашиваете следующие 20 обращений). Если окажется, что кеширование сэкономит ваше время, вы можете сделать это через прокси (например, nginx). - person Christian Grün; 22.04.2016
comment
Последний комментарий: я забыл упомянуть, что действительно будет какое-то кеширование, потому что при каждом запросе, который выполняется более одного раза, результаты будут извлечены с диска и, таким образом, перемещены в основную память. Однако явного кэширования в BaseX нет по причинам, упомянутым выше. - person Christian Grün; 23.04.2016
comment
Привет Кристиан. Спасибо за комментарии. Я собираюсь кое-что попробовать и свяжусь с вами (здесь или в списке рассылки). Еще раз спасибо! - person Bram Vanroy; 23.04.2016

Надеюсь, я понял это правильно.

Вместо того, чтобы позволить браузеру "изначально" отправить ФОРМУ, не делайте этого: вместо этого напишите JS-код, который делает это. Другими словами (я не проверял это, поэтому интерпретируйте как псевдокод):

<form action="results.php" onsubmit="return false;">
  <input name="my-input" type="text">
  <input type="submit" value="submit">
</form>

Итак, теперь, когда нажата кнопка «Отправить», ничего не произойдет.

Очевидно, вы хотите, чтобы ваша форма была отправлена ​​POST, поэтому напишите JS, чтобы прикрепить обработчик кликов к этой кнопке отправки, собрать значения из всех полей ввода в форме (на самом деле это НЕ так страшно, как кажется; проверьте ссылку ниже), и отправьте его на сервер, сохранив при этом ссылку на запрос (проверьте 2-ю ссылку ниже), чтобы вы могли прервать его (и, возможно, сигнализировать серверу о завершении работы) при нажатии кнопки отмены (в качестве альтернативы вы можете просто отказаться от него, не заботясь о результатах).

Отправить форму с помощью jQuery

Отменить запросы Ajax с помощью jQuery

В качестве альтернативы, чтобы сделать эту HTML-разметку «более четкой» по сравнению с ее функциональностью, подумайте о том, чтобы вообще не использовать тег FORM: в противном случае то, что я предложил, сбивает с толку его использование (почему он там, если он не используется; понимаете, я имею в виду?). Но не отвлекайтесь на это предложение, пока не заставите его работать так, как вы хотите; это необязательно и тема для другого дня (это может даже относиться к вашей изменяющейся архитектуре всего сайта).

ОДНАКО, над чем задуматься: что делать, если форма-пост уже дошел до сервера и сервер уже начал его обрабатывать и какие-то "мировые" изменения уже внесены? Возможно, ваша подпрограмма получения результатов не изменяет данные, так что все в порядке. Но этот подход, вероятно, нельзя использовать с POST-сообщениями об изменении данных, ожидая, что «мир» не изменится, если будет нажата кнопка отмены.

Надеюсь, это поможет :)

person Hari Lubovac    schedule 25.04.2016

Пользователю не обязательно испытывать это синхронно.

  1. Клиент отправляет запрос
  2. Сервер получает запрос клиента и присваивает ему идентификатор
  3. Сервер «запускает» поиск и отвечает страницей с нулевыми данными и идентификатором поиска.
  4. Клиент получает страницу «заполнитель» и начинает проверять готовность результатов на основе идентификатора (с помощью чего-то вроде опроса или веб-сокетов).
  5. После завершения поиска сервер отвечает результатами при следующем опросе (или уведомляет клиента напрямую при использовании веб-сокетов).

Это нормально, когда производительность не является узким местом, а характер обработки делает приемлемым более длительное время ожидания. Подумайте об агрегаторах поиска рейсов, которые обычно работают в течение 30-90 секунд, или о генераторах отчетов, которые нужно планировать и запускать еще дольше!

Вы можете сделать процесс менее неприятным, если не будете блокировать действия пользователей, держать их в курсе хода поиска и начинать показывать результаты по мере их поступления, если это возможно.

person Oleg    schedule 21.04.2016
comment
Что ж, это здорово. К сожалению, я ничего не знаю о веб-сокетах и ​​о том, как реализовать эту идею. С наградой в 200, я думаю, будет справедливо попросить какой-нибудь пример кода из запроса, пока не появятся результаты? - person Bram Vanroy; 21.04.2016

Вы должны сначала решить это концептуально, прежде чем писать какой-либо код. Вот некоторые вещи, которые приходят на ум навскидку:

Что означает освобождение ресурсов на сервере?

Что представляет собой изящное прерывание, которое освободит ресурсы?

Достаточно ли убить процесс PHP, ожидающий результатов запроса? Если это так, то маршрут, предложенный RandomSeed, может быть интересным. Просто имейте в виду, что это будет работать только на одном сервере. Если у вас есть несколько серверов с балансировкой нагрузки, у вас не будет возможности убить процесс на другом сервере (по крайней мере, не так просто).

Или вам нужно отменить запрос к базе данных из самой базы данных? В этом случае более интересен ответ, предложенный Кристианом Грюном.

Или дело в том, что нет изящного выключения, и вы должны заставить все умереть? Если это так, это кажется ужасно хакерским.

Не все клиенты будут явно прерваны

Некоторые клиенты собираются закрыть браузер, но их последний запрос не будет выполнен; некоторые клиенты потеряют подключение к Интернету и оставят службу зависшей и т. д. Нет гарантии, что вы получите запрос «отмена», когда клиент отключается или уходит.

Вы должны решить, жить ли с потенциально нежелательным поведением или внедрить дополнительное отслеживание активного состояния, например. клиентский пинг-сервер для поддержки активности.

Примечания

  • 30 секунд или больше времени запроса потенциально долго, есть ли лучший инструмент для этой работы; так что вам не придется решать это с помощью взлома, как это?

  • вы ищете функции параллельной системы, но не используете параллельную систему; если вы хотите параллелизм, используйте для него лучший инструмент/среду, например. Эрланг.

person Unix One    schedule 24.04.2016