Обработка длительных и длительных вызовов ajax в веб-приложении с помощью сеансов

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

Эта проблема

У меня есть веб-приложение, все написанное на javascript, которое связывается с сервером через API. В этом приложении есть несколько «массовых действий», выполнение которых занимает много времени. Я хочу выполнить их безопасным способом, убедившись, что время ожидания сервера не истекло, и с обширной обратной связью с пользователем, чтобы он / она знали, что происходит.

Обычный подход

Как я вижу в своем исследовании, рекомендуемый метод выполнения — запуск фонового процесса на сервере и заставить его писать куда-нибудь, как он работает, чтобы вы могли отправлять запросы на проверку и давать обратную связь пользователю. Так как я использую php в бэк-энде, подход будет более или менее описанным здесь: http://humblecontributions.blogspot.com.br/2012/12/how-to-run-php-process-in-background.html

Добавление нескольких реквизитов

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

Я хочу, чтобы он работал «из коробки» на (почти) любом сервере с типичной поддержкой WordPress, даже если в конечном итоге решение оказалось немного медленнее.

Мой подход

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

Поэтому, когда браузер в первый раз отправляет запрос на запуск процесса, он запускает только небольшой шаг и возвращает полезную информацию, чтобы дать пользователю некоторую обратную связь. Затем браузер выполняет другой запрос и повторяет его, пока сервер не сообщит, что процесс завершен.

Чтобы сделать это, я бы сохранил этот объект в сеансе, поэтому первый запрос даст мне идентификатор, а следующие запросы отправят этот идентификатор на сервер, чтобы он манипулировал тем же объектом.

Вот концептуальный пример:

class LongProcess {

function __construct() {

$this->id = uniqid();
$_SESSION[$this->id] = $this;
$this->step = 1;
$this->total = 100;

}


function run() {
// do stuff based on the step you are in
$this->step = $this->step + 10;
if ($this->step >= $this->total)
return -1;
return $this->step;
}

}

function ajax_callback() {

session_start();

if (!isset($_POST['id']) || empty($_POST['id'])) {
$object = new LongProcess();
} else {
$object = $_SESSION[$_POST['id']];
}

$step = $object->run();

echo json_encode([
'id' => $object->id,
'step' => $return,
'total' => $object->total
]);

}

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

    function recursively_ajax(session_id)
{
$.ajax({
type:"POST",
async:false, // set async false to wait for previous response
url: "xxx-ajax.php",
dataType:"json",
data:{
action: 'bulk_edit',
id: session_id
},
success: function(data)
{
updateFeedback(data);
if(data.step != -1){
recursively_ajax(data.id);
} else {
updateFeedback('finish');
}
}
});
}

$('#button').click(function() {
recursively_ajax();
});

Конечно, это всего лишь подтверждение концепции, я даже не использую jQuery в реальном коде. Это просто чтобы выразить идею.

Обратите внимание, что этот объект, который хранится в сеансе, должен быть очень легким объектом. Любые обрабатываемые данные должны храниться в базе данных или файловой системе и ссылаться на них только в объекте, чтобы он знал, где искать вещи.

Типичным случаем будет обработка большого файла CSV. Файл будет храниться в файловой системе, а объект будет хранить указатель на последнюю обработанную строку, чтобы он знал, с чего начать в следующем запросе.

Объект также может возвращать более подробный журнал, описывающий все, что было сделано, и сообщающий об ошибках, чтобы пользователь имел полное представление о том, что было сделано.

Интерфейс, который я думаю, был бы отличным, это индикатор выполнения с кнопкой «посмотреть детали», которая открыла бы текстовое поле с этим подробным журналом.

Имеет ли это смысл?

Так что теперь я спрашиваю. Как это выглядит? Это жизнеспособный подход?

Есть ли лучший способ сделать это и убедиться, что он будет работать на очень ограниченных серверах?

2

Решение

Ваш подход имеет несколько недостатков:

  1. Ваши тяжелые запросы могут блокировать другие запросы. Обычно у вас есть ограничение числа одновременных процессов PHP для обработки веб-запросов. Если ограничение равно 10, и все слоты заняты обработкой ваших сложных запросов, ваш веб-сайт не будет работать, пока некоторые из этих запросов не завершат освобождение слота для другого легкого запроса.

  2. Вы (вероятно) не сможете оценить, сколько времени потребуется, чтобы закончить один шаг. В зависимости от нагрузки на сервер это может занять 5 или 50 секунд. И 50 секунд, вероятно, превысят лимит времени выполнения на большинстве общих хостингов.

  3. Эта задача будет контролироваться клиентом — любое прерывание со стороны клиента (проблемы с сетью, закрытие вкладки браузера) будет прерывать задачу.

  4. В зависимости от серверного сеанса использование сеанса для сохранения текущего состояния может привести к ошибкам состояния гонки — одновременный запрос от одного и того же клиента может перезаписать изменения в сеансе, выполненные фоновой задачей. По умолчанию PHP использует блокировку для сеанса, поэтому это не должно быть так, но если кто-то использует альтернативный бэкэнд для сеансов (DB, Redis) без блокировки, это приведет к серьезным и трудным для отладки ошибок.

Здесь есть очевидный компромисс. Для небольших веб-сайтов, где упрощение установки и настройки является приоритетом, ваш подход в порядке. В любом другом случае я бы использовал простую очередь на основе cron для выполнения задач в фоновом режиме и использовал AJAX-запрос только для получения текущего состояния задачи. До сих пор я не видел хостинга без поддержки cron, и добавление задачи в cron не должно быть таким сложным для конечного пользователя (с соответствующей документацией).

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

2

Другие решения

Спасибо за весь вклад. Я просто хочу документировать здесь некоторые очень хорошие ответы, которые я получил.

Некоторые плагины WordPress, называемые Woocommerce, содержат код из библиотеки «WP Background Processing», который больше не поддерживается, но реализует подход Cron с некоторыми важными улучшениями. Смотрите это сообщение в блоге:

https://deliciousbrains.com/background-processing-wordpress/

Фактическая библиотека живет здесь: https://github.com/A5hleyRich/wp-background-processing

Хотя это библиотека для WordPress, я думаю, что подход подходит для любой ситуации.

Для WordPress есть также библиотека под названием Action Scheduler, которая не только настраивает процессы в фоновом режиме, но и позволяет планировать их. Это стоит посмотреть:

https://github.com/Prospress/action-scheduler

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector