Я выбрал в качестве примера формы обратной связи, потому что большинство веб-разработчиков с ними так или иначе сталкивались и поэтому как нельзя лучше поймут все преимущества отказа от синхронного выполнения обработки запроса от формы.
Для тех, кому удобнее видео-формат, смотрите видео ниже. Если же вы предпочитаете текстовый формат, крутите ниже.
Как работают формы обратной связи
Как правило, форма обратной связи работает следующим образом:
- Вы вводите свои данные и нажимаете "Отправить"
- Сервер принимает этот запрос и начинает его обработку. Сначала происходит валидация введенных вами данных — эта операция быстрая и не требует большого времени на выполнение.
- Далее запускается логика отправки данных на почту, в CRM и так далее. И именно здесь у нас возникает самая длительная задержка, потому что нужно обратиться к почтовому серверу, отправить данные, дождать ответа, обратиться к API CRM, отправить запрос, дождаться ответа и так далее.
А что, если отправка происходит не на 1 почтовый ящик, а на 3 (например, в техподдержку, менеджеру и гендиректору, потому что он тоже хочет все контролировать). В этом случае время ожидания увеличится в несколько раз.
Как ускорить обработку?
Для того, чтобы все работало, как ожидается и при этом сайт не подвисал на несколько секунд при отправке формы, необходимо выносить всю длительную логику в асинхронную задачу, чтобы она не блокировала выполнение остального кода.
Один из способов это сделать — очередь: вы отправляете задачу в очередь, а специальный процесс (он же демон или воркер) — обработчик очереди выполнит ее, когда будет в очередной раз опрашивать очередь.
Как настроить очередь в Laravel
Если мы заглянем в файл config/queue.php, то заметим, что в настройке connections находятся 5 различных видов соединений:
- Синхронная очередь. По сути — это имитация или некоторый "совсем базовый" вид очередей, так как одна из сильных сторон очередей — асинхронность выполнения задач и возможность выполнить задачу с отсрочкой по времени.
- Очередь на основе таблицы в базе данных
- Очередь на основе пакета beanstalkd.
- Очередь на основе сервиса очередей от Amazon
- Очередь на основе сервера 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.
Вы должны авторизоваться, чтобы оставлять комментарии.