Плохой UX в веб-приложениях, выполняющих интенсивные задачи (и как этого избежать с помощью очередей)

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

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

Это знакомый HTTP-цикл «запрос/ответ», который показан на этой диаграмме:

Типичная архитектура веб-приложения

Хороший UX требует, чтобы веб-серверы быстро реагировали. По этой причине, интенсивная задача не должна быть втиснута в цикл запрос/ответ.

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

Мы можем добавить очередь сообщений к архитектуре, чтобы добиться этого эффективно.

Архитектура веб-приложения с очередью сообщений

В этой статье мы рассмотрим общие этапы реализации очереди сообщений в веб-приложении с использованием Vue и Laravel.

Примечание: эта статья изначально была опубликована здесь, в блоге разработчиков Vue.js 08.01.2019.

Выход из цикла запрос/ответ

Допустим, мы создаем приложение, которое обрабатывает файлы CSV и записывает данные в базу данных. Обработка особенно большого файла CSV может занять несколько минут.

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

Клиент

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

CSVUpload.vue

<template>
  <div>
    <div v-if="message">{{ message }}</div>
    <form id="upload" enctype="multipart/form-data" @submit.prevent="submit">
      <p>Please select the file you'd like to upload.</p>
      <input type="file" name="csv" />
      <input type="submit" value="Upload" />
    </form>
  </div>
</template>

Мы будем использовать HTTP POST для отправки файла. Поскольку мы не собираемся обрабатывать CSV в цикле запрос/ответ, мы не ожидаем, что конечный результат из ответа. Вместо этого мы просто хотим, чтобы сервер сообщил нам, что файл был получен.

submit(event) {
  axios.post("/upload", new FormData(event.target))
    .then(res => {
      this.message = res.status;
    });
}

Сервер

На сервере у нас будет контроллер, который обрабатывает этот запрос на загрузку файла. Мы уточним логику в следующем разделе, но важно отметить, что мы прикрепляем код HTTP 202 (Accepted) к ответу. Это уместно, когда вы хотите сообщить клиенту, что запрос получен, но еще не выполнен.

Приложение/Http/Контроллеры/CSVUploadController.php

public function store(Request $request) 
{
  if ($request->hasFile('csv')) {
    
    return response("File received for processing.", 202);
  } else {
    return response("No file provided.", 400);
  }
}

Использование очереди сообщений

Когда файл получен веб-сервером, как мы можем его обработать вне цикла запрос/ответ? Здесь мы хотим использовать очередь сообщений.

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

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

Примеры программного обеспечения очереди сообщений включают:

  • бобовый стебель
  • Amazon SQS (облачная очередь сообщений)
  • Redis (по сути не является очередью сообщений, но отлично работает как таковая)

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

Очереди Laravel

Очереди Laravel упростите взаимодействие веб-приложения Laravel с очередью сообщений.

Вот общий обзор того, как они работают — позже я приведу конкретный пример.

  1. Запустите очередь сообщений. Сообщите Laravel, где находится и как получить к нему доступ через config/queues.php файл конфигурации.
  2. Запустите рабочий процесс очереди. Это посредник между веб-приложением и очередью сообщений, который будет прослушивать новые задания и помещать их в очередь. Поскольку нам нужно обрабатывать задачи очереди асинхронно, это будет выполняться как отдельный процесс для вашего веб-приложения.
  3. Отправьте «задание» и рабочий процесс очереди (например, некоторый код, который вы хотите выполнить — мы лучше определим задания ниже)
  4. Прослушайте событие, содержащее результат задания (необязательно).

Например, мы можем использовать Redis в качестве очереди сообщений. Laravel включает готовые драйверы для этого, так что это просто вопрос запуска Redis на сервере и сообщения Laravel порта/пароля в config/queues.php.

Laravel предоставляет рабочий процесс очереди из коробки через консоль Artisan. Откройте вкладку терминала и выполните:

$ php artisan queue:work redis

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

Работа

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

Laravel предоставляет Job класс, в который вы поместили свой код. Используйте Artisan, чтобы создать его:

$ php artisan make:job ProcessCSV

handle метод вызывается при запуске этого задания, так что именно туда мы помещаем логику задачи.

Приложение/Работа/ProcessCSV.php

public function handle()
{
  
}

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

Приложение/Http/Контроллеры/CSVUploadController.php

public function store(Request $request) 
{
  if ($request->hasFile('csv')) {
    ProcessCSV::dispatch($request->file("csv"));
    return response("File received for processing!", 202);
  } else {
    return response("No file provided.", 400);
  }
}

Использование асинхронного протокола для информирования пользователя о результате

Наш начальный 202 Accepted сказал клиенту, что мы работаем над задачей, но нам, вероятно, нужно сообщить им результат, когда задача будет выполнена.

Поскольку выполнение задачи может занять много времени, было бы лучше использовать асинхронный протокол, такой как электронная почта или SMS, для информирования о результате, чтобы пользователь мог продолжать использовать свой браузер для прокрутки Facebook или Reddit в течение нескольких минут и не надо сидеть в ожидании.

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

Клиент

Давайте изменим форму на клиенте, чтобы пользователь мог указать свой адрес электронной почты:

<form id="upload" enctype="multipart/form-data" @submit.prevent="submit">
  <p>Please select the file you'd like to upload. Provide an email address and we'll inform you of the result and spam you later.</p>
  <input type="file" name="csv" />
  <input type="email" name="email" />
  <input type="submit" value="Upload" />
</form>

Сервер

Теперь, когда мы обработаем первоначальный запрос, мы можем передать адрес электронной почты в задание:

public function store(Request $request) 
{
  if ($request->hasFile('csv')) {
    ProcessCSV::dispatch($request->file("csv"), $request->email);
    return response("File received for processing!", 202);
  } else {
    return response("No file provided.", 400);
  }
}

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

Мы можем прослушать это событие и использовать его для отправки уведомления. И почему бы не создать еще одно задание для отправки электронной почты!

Приложение/Провайдеры/AppServiceProvider.php

Queue::after(function (JobProcessed $event) {
  $result = ... 
  SendEmail::dispatch($event->data["email"], $result);
});

Заворачивать

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

Очереди Laravel отлично подходят для использования возможностей очередей сообщений в веб-приложении. Есть много других функций, которые я здесь не рассмотрел, в том числе бесплатный Laravel. Горизонт панель инструментов для управления вашей очередью.


Станьте старшим разработчиком Vue в 2020 году.

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

Учить больше


Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *