Ответ
Помимо очередей сообщений (RabbitMQ, Kafka), для асинхронной обработки в распределённых системах применяются следующие механизмы:
-
Event Sourcing и CQRS
- Как: Состояние приложения определяется последовательностью событий. Команды генерируют события, которые сохраняются в логе. Запросы обрабатываются отдельными моделями чтения, обновляемыми асинхронно.
- Почему: Позволяет воспроизвести состояние системы в любой момент, обеспечивает надёжное аудит-логирование и естественным образом разделяет нагрузку на запись и чтение.
- Пример (концептуальный):
// Команда (синхронно) OrderService->placeOrder(command) -> emits OrderPlacedEvent // Событие (асинхронно) EmailProjector->onOrderPlaced(event) -> updates read model ReportingService->onOrderPlaced(event) -> updates dashboard
-
Асинхронные RPC/Вызовы (gRPC, HTTP с колбэками)
- Как: Сервис A отправляет запрос к сервису B и немедленно получает подтверждение (ACK), не дожидаясь полной обработки. Результат или уведомление о завершении доставляются позже через отдельный канал (webhook, очередь событий).
- Почему: Освобождает вызывающую сторону от длительного блокирования, повышая отзывчивость.
-
Шаблон «Отложенный результат» (Future/Promise) и реактивные потоки
- Как: Вызов возвращает объект-обещание (Future, CompletableFuture в Java, Promise в JS), представляющий результат, который будет вычислен позже. Реактивные библиотеки (Project Reactor, RxJava) позволяют строить цепочки асинхронных операций.
- Почему: Удобная абстракция для работы с асинхронностью в коде, комбинирование операций без "callback hell".
-
Пример (Java с CompletableFuture):
CompletableFuture<User> userFuture = userService.getUserAsync(userId); CompletableFuture<Order> orderFuture = orderService.getOrderAsync(orderId); userFuture.thenCombine(orderFuture, (user, order) -> { // Асинхронная обработка, когда оба результата готовы return processOrder(user, order); }).thenAccept(result -> System.out.println("Done: " + result));
-
База данных как очередь (SELECT ... FOR UPDATE SKIP LOCKED)
- Как: Задачи хранятся в таблице БД со статусом. Несколько воркеров конкурентно выбирают задачи с помощью запросов, использующих пессимистичные блокировки с пропуском заблокированных строк.
- Почему: Использует транзакционность БД для гарантированной доставки, не требует дополнительных инфраструктурных компонентов. Подходит для невысоких нагрузок или как fallback.
Ключевой выбор зависит от требований: гарантии доставки (очереди, Event Sourcing), низкие задержки (асинхронные RPC), композиция операций (реактивные потоки) или простота (база данных).