Как реализовать асинхронное взаимодействие с партнером через Kafka
Что проверяют
Понимаете ли вы, что асинхронная интеграция это не «отправил в топик и забыл», а договор о формате, гарантиях доставки и обработке сбоев. Следом почти всегда спрашивают, что будет, если партнёр получит сообщение дважды, и как не потерять событие, когда база уже закоммитилась, а Kafka недоступна.
Как отвечать
- контракт: отдельные топики на запросы и ответы, схема в Avro или Protobuf через Schema Registry, правила эволюции схемы. В заголовки кладите
correlationIdи идентификатор сообщения, чтобы связать ответ с запросом; - ключ и порядок: порядок гарантирован только внутри партиции, поэтому ключ выбирают по сущности, например номер заказа. Тогда события одного заказа придут по очереди;
- продюсер:
acks=allиenable.idempotence=true, начиная с Kafka 3.0 это значения по умолчанию. Результатsend()проверяйте в колбэке или черезCompletableFutureуKafkaTemplate; - атомарность с базой: паттерн Transactional Outbox. Событие пишется в таблицу в той же транзакции, что и бизнес-данные, а отдельный процесс или Debezium публикует его в топик;
- консюмер: смещение фиксируется только после обработки, в Spring Kafka это делает контейнер, автокоммит там по умолчанию выключен. Получается at-least-once, поэтому обработка идемпотентна: таблица обработанных идентификаторов или уникальный ключ;
- ошибки:
DefaultErrorHandlerс backoff иDeadLetterPublishingRecovererили неблокирующие повторы через@RetryableTopic.
Для внешнего партнёра добавьте SASL/SSL, ACL на топики и отдельные учётные записи.
Где спотыкаются
Вызывают send() и не смотрят на результат, а недоступность брокера всплывает через неделю пропавшими заказами. Обратная крайность: send().get() на каждое сообщение, и пропускная способность падает в разы.
Включают автокоммит и отдают обработку в свой пул потоков. Смещение уходит вперёд раньше, чем обработка закончилась, и при падении сервиса часть сообщений теряется.
Не думают о «ядовитом» сообщении. Запись, которая не десериализуется, консюмер перечитывает раз за разом и не двигается дальше по партиции. Нужен ErrorHandlingDeserializer и топик для мёртвых писем.