Обработка длительных и длительных вызовов ajax в веб-приложении с помощью сеансов - PullRequest
0 голосов
/ 20 мая 2018

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

Проблема

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

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

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

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

Поскольку Im разрабатываетсяпроект с открытым исходным кодом (плагин 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 ]

0 голосов
/ 21 мая 2018

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

Некоторые плагины 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 голосов
/ 20 мая 2018

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

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

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

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

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

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

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

...