Очереди в Laravel или почему ваш сайт тормозит

Замечали, как на некоторых сайтах долго "думает" форма обратной связи? Вы спросите, какое это имеет отношение к очередям? Давайте обо всем по порядку.

}

Автор материала

Артем Зернов. Веб-разработчик, создатель проекта Лектория, эксперт MODX Revolution, директор веб-студии OpenColour. Youtube-канал OpenModx.

6 минут на прочтение
Теги по теме:

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

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

Как работают формы обратной связи

Как правило, форма обратной связи работает следующим образом:

  1. Вы вводите свои данные и нажимаете "Отправить"
  2. Сервер принимает этот запрос и начинает его обработку. Сначала происходит валидация введенных вами данных — эта операция быстрая и не требует большого времени на выполнение.
  3. Далее запускается логика отправки данных на почту, в CRM и так далее. И именно здесь у нас возникает самая длительная задержка, потому что нужно обратиться к почтовому серверу, отправить данные, дождать ответа, обратиться к API CRM, отправить запрос, дождаться ответа и так далее.

А что, если отправка происходит не на 1 почтовый ящик, а на 3 (например, в техподдержку, менеджеру и гендиректору, потому что он тоже хочет все контролировать). В этом случае время ожидания увеличится в несколько раз.

Как ускорить обработку?

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

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

Как настроить очередь в Laravel

Если мы заглянем в файл config/queue.php, то заметим, что в настройке connections находятся 5 различных видов соединений:

  1. Синхронная очередь. По сути — это имитация или некоторый "совсем базовый" вид очередей, так как одна из сильных сторон очередей — асинхронность выполнения задач и возможность выполнить задачу с отсрочкой по времени.
  2. Очередь на основе таблицы в базе данных
  3. Очередь на основе пакета beanstalkd.
  4. Очередь на основе сервиса очередей от Amazon
  5. Очередь на основе сервера redis

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

1. Подготовим таблицу БД для очереди

Запускаем в консоли встроенную команду, которая создаст в базе данных таблицу для хранения отложенных задач (очередь)

php artisan queue:table
php artisan migrate

2. Указываем вид соединения

В файле .env находим настройку QUEUE_CONNECTION и указываем соединение для работы с очередями.

QUEUE_CONNECTION=database

Если данной настройки не было в файле .env ранее, то скопируйте строку выше и добавьте в конец файла или между любыми другими настройками.

3. Создаем асинхронную задачу (Job)

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

Создаем отдельный Job-класс для нашей долгой задачи, выполнив следующую команду:

php artisan make:job MyVeryLongJob

Как вы понимаете, имя MyVeryLongJob вы можете заменить на свое. Как правило, я в своих проектах, оставляю в названии класса его принадлежность к определенной сущности. Например, если это Job, то в конце имени класса будет "Job". Если это контроллер, то это может быть, например, "MainUserController".

4. Внедряем необходимые зависимости и аргументы в Job

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

class MyVeryLongJob implements ShouldQueue {
...
    public $user;
    public $service;
    
    public function __construct(User $user, MyVeryImportantService $service){
        $this->user = $user;
        $this->service = $service;
    }
...
}

5. Опишите логику вашего джоба

В методе handle() класса App\Jobs\MyVeryLongJob опишите всю необходимую логику работы вашей задачи (джоба), используя внедренные зависимости через $this->.

6. Запуск джоба

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

...
MyVeryLongJob::dispatch([АРГУМЕНТЫ И ЗАВИСИМОСТИ]);
...

Обратите внимание, что при внедрении зависимости от какого-либо сервиса, добавленного через сервис-контейнер, нет необходимости указывать его в аргументах метода dispatch().

Например, для того джоба, в конструкторе которого мы в качестве примера указали зависимости в пункте 4, отправка задачи в очередь может выглядеть следующим образом:

...
$user = User::find(1);
MyVeryLongJob::dispatch($user);
...

Обратите внимание, что в аргументах метода dispatch() мы не передаем сервис MyVeryImportantService $service

7. Запуск воркера

Ну и наконец, чтобы наши отправляемые в очередь задачи были запущены и обработаны, необходимо запустить отдельный специальный процесс (воркер) при помощи следующей консольной команды:

php artisan queue:work --queue=default

В качестве параметра --queue мы указали очередь под именем "default" — это именно то имя очереди, в которую отправляются задачи по-умолчанию, если мы явно не задали имя очереди. Не путайте с соединением. Напротив, в каждом соединении в файле config/queue.php с помощью параметра queue задано имя очереди по-умолчанию (то самое "default", которое мы указали в параметре команды php artisan queue:work).

Заключение

Все, что я описал выше — это всего лишь основы работы с очередями. Так сказать, мануал для быстрого погружения, чтобы попробовать здесь и сейчас. Попробуйте сделать одну и ту же задачу с очередью и без нее. Добавьте в код логики что-нибудь вроде sleep(5), чтобы заметить, насколько долго выполняется задача. И после этого сравните, как ведет себя страница вашего сайта при работе с очередями и без. Разница будет очевидной!

Удачи в веб-разработке. Задавай вопросы в комментариях, подписывайся на Youtube-канал Lectoria и обязательно подписывайся на наши соцсети: Instagram, VK, Facebook.

Комментарии

    Вы должны авторизоваться, чтобы оставлять комментарии.

    Мы используем куки на нашем сайте. Продолжая просмотр, вы соглашаетесь с условиями пользовательского соглашения
    Пожалуйста, подождите. Процесс оформления заказа может занимать до 30 секунд.