RabbitMQ — это система управления очередями сообщений с открытым исходным кодом, написанная на Erlang. Она предназначена для эффективной обработки и передачи сообщений между компонентами приложений. RabbitMQ позволяет разъединять издателей (producer) и потребителей (consumer) сообщений, благодаря чему сообщения могут отправляться и получать асинхронно, увеличивая производительность системы.

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

RabbitMQ использует протокол AMQP (Advanced Message Queuing Protocol), который гарантирует надежную доставку сообщений. AMQP поддерживает различные механизмы маршрутизации и управления очередями, позволяя создавать гибкие архитектуры для взаимодействия микросервисов. RabbitMQ предоставляет поддержку обменников и очередей, что делает его мощным и настраиваемым инструментом для распределенной обработки сообщений.

Схема работы RabbitMQ

Пояснения к схеме:

Direct Exchange:

Производитель (Publisher) отправляет сообщение с routing key "key1".

Direct Exchange (DE) сравнивает routing key с binding key очереди.

Если совпадение найдено (например, с Queue 1), сообщение отправляется в эту очередь.

Если совпадений нет (например, с Queue 2), сообщение игнорируется.

Topic Exchange:

Производитель (Publisher) отправляет сообщение с routing key "a.b.c".

Topic Exchange (TE) использует шаблонные сопоставления (wildcard).

Если binding key очереди соответствует шаблону (например, "a.#"), сообщение отправляется в эту очередь.

Если binding key соответствует другому шаблону (например, "*.b.*"), сообщение отправляется в другую очередь.

Fanout Exchange:

Производитель (Publisher) отправляет любое сообщение.

Fanout Exchange (FE) отправляет сообщение во все связанные очереди без проверки routing key.

Приведём несколько примеров где может применяться RabbitMq:

1. Микросервисная архитектура

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

2. Обработка больших объемов данных

При обработке больших объемов данных RabbitMQ помогает организовать конвейеры обработки данных. Например, система может получать данные из множества источников (сенсоры, веб-запросы, логи), которые затем отправляются в RabbitMQ. Далее рабочие процессы могут забирать эти сообщения и обрабатывать их параллельно, обеспечивая высокую производительность и масштабируемость.

3. Асинхронные задачи

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

4. Балансировка нагрузки

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

5. Мониторинг и логирование

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

Перейдем к более частному вопросу. Представим пример использования RabbitMQ в реальной системе.

Представим себе систему управления заказами в крупном интернет-магазине. В такой системе могут возникать следующие сценарии использования RabbitMQ:

Сценарий 1: Обработка заказа

Когда пользователь размещает заказ, информация об этом отправляется в RabbitMQ. Различные сервисы (например, служба оплаты, служба доставки, служба учета запасов) подписаны на соответствующие очереди и начинают выполнять свои задачи после получения сообщения. Это обеспечивает асинхронность и параллелизм в процессе обработки заказа.

Сценарий 2: Уведомления пользователям

После успешного размещения заказа система должна уведомить пользователя о статусе его заказа. Для этого можно использовать RabbitMQ для отправки сообщений в службу уведомления, которая будет рассылать письма или SMS-сообщения. Это позволит отделить процесс уведомления от основной бизнес-логики и сделать его более надежным и масштабируемым.

Сценарий 3: Аналитика и отчетность

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

Далее давайте приведём простейший пример использования RabbiMq в Framework Laravel.

В Laravel будем использовать популярный пакет «RabbitMQ Queue driver for Laravel» (https://github.com/vyuldashev/laravel-queue-rabbitmq)

На установке самого RabbitMq и добавления пакета в Laravel останавливаться не будем. Это всё можно посмотреть в официальной документации.

Задача: После оформления заказа, отправлять письмо об успешном оформлении заказа.

1.Определение очередей в конфигурации

В файле config/queue.php вы задаёте несколько очередей для различных типов заданий. Например:

'rabbitmq' => [
            'driver' => 'rabbitmq',
            'queue' => env('RABBITMQ_QUEUE', 'default'),
            'connection' => PhpAmqpLib\Connection\AMQPLazyConnection::class,
            'hosts' => [
                [
                    'host' => env('RABBITMQ_HOST', 'laravel-rabbitmq'),
                    'port' => env('RABBITMQ_PORT', 5672),
                    'user' => env('RABBITMQ_USER', 'guest'),
                    'password' => env('RABBITMQ_PASSWORD', 'guest'),
                    'vhost' => env('RABBITMQ_VHOST', '/'),
                ],
            ],
            'worker' => env('RABBITMQ_WORKER', 'default'),
        ],

Обязательно в .env изменить параметр очередей на QUEUE_CONNECTION=rabbitmq. Также тут могут быть заданы переменные подключения RabbitMq

2.Создание классов-обработчиков.

Создаём класс обработчика php artisan make:job SendOrderEmailJob

SendOrderEmailJob.php
 <?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;
use App\Mail\OrderConfirmationMail;

class SendOrderEmailJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    private $order;

    public function __construct(\App\Models\Order $order)
    {
        $this->order = $order;
    }

    public function handle()
    {
        Mail::to($this->order->email)->send(new OrderConfirmationMail($this->order));
    }

}

Класс, который отправляет письмо: OrderConfirmationMail

<?php

declare(strict_types=1);

namespace App\Mail;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class OrderConfirmationMail extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    public function build()
    {
        return $this->view('emails.order_confirmation')
            ->with([
                'order' => $this->order,
            ]);
    }

Шаблон письма нужно создать в resources/views/emails/order_confirmation.blade.php

3.В нужном месте добавляем задание в очередь с названием send_email.

Возможно это может быть контроллер:

<?php
namespace App\Http\Controllers;

use App\Models\Order;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class OrderController extends Controller
{
    public function store(Request $request)
    {
        $order = new Order();
        $order->fill($request->all());
        $order->save();

        try {
            dispatch(new \App\Jobs\SendOrderEmailJob($order))->onQueue('send_email');
        } catch (\Exception $e) {
            Log::error('Ошибка публикации сообщения в очередь: ' . $e->getMessage());
        }

        return response()->json(['message' => 'Заказ успешно оформлен'], 201);
    }
}

Всё готово! Теперь при оформлении заказа, письмо сразу не отсылается, а добавляется в очередь. В примере мы выделили отправку в отдельную очередь send_email, для наглядности.

Что бы обработать очередь надо запустить worker:

php artisan queue:work --queue=send_email

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

На примере интеграция RabbitMQ в Laravel мы можем сделать вывод — это быстрый и понятный процесс. Достаточно установить пакет, настроить очередь и создать обработчик, чтобы сделать приложение более масштабируемым и отзывчивым. Попробуйте использовать RabbitMQ в своем проекте, и вы сами убедитесь, насколько это просто!