Привет, Хабр!
Меня зовут Михаил, я являюсь ФинТех системным аналитиком, исполняющим роль архитектора на проектах. И сегодня я хочу поделиться материалами по Kafka, которые подготовил для обучения сотрудников из своей команды. В материале постарался отразить самые важные технические моменты по Kafka, которые будут подкреплены наглядными кейсами и примерами использования. Материал будет полезным как для начинающих, так и для продвинутых пользователей. Желаю приятного прочтения?
План статьи будет следующий:
-
Сначала повторим базу и вспомним что такое Kafka (полезно для начинающих) [1]
-
Далее обсудим особенности конфигурации Консюмера и Продюсера (полезно для продвинутых) [2]
-
После чего рассмотрим лучшие практики работы с Kafka (полезно для продвинутых) [3]
-
И под конец разберем прикладные кейсы применения Kafka в боевых условиях (полезно для всех) [4]
Потоковая обработка данных — сбор аналитики в реальном времени, обработка логов [4.1]
Кросс‑сервисное взаимодействие между несколькими приложениями‑производителями и ‑потребителями [4.2]
Основа для шины данных [4.3]
Асинхронное взаимодействие между системами [4.4]
Когда не стоит применять Kafka [4.5]
№1 ЧТО ТАКОЕ KAFKA
Apache Kafka — опенсорс распределенная система брокеров сообщений, работающая по модели pub/sub
Я попытался подобрать наиболее емкое и одновременное информативное определение для данной технологии. И чтобы слова, собранные в дефиницию не выглядел как просто набор красивых слоганов, предлагаю детальнее взглянуть на части определения.
Несколько интересных фактов о том, когда и для чего была создана Kafka:
Apache Kafka была разработана в компании LinkedIn в 2011 году и с тех пор стала популярной платформой для обработки потоковых данных в реальном времени.
Для большей конкретики скажу, что в LinkedIn был следующий кейс обработки потоковых данных — это была отправка и получение логов в реальном времени в рамках постоянного подключения между Системой‑производителем и Cистемой‑потребителем в целях оперативного отслеживания состояния Системы‑производителя.
Kafka написана на языках программирования Java и Scala.
Но при этом для взаимодействия с Kafka существует Kafka API — программный интерфейс из коробки, реализованный для большинства популярных языков программирования, что позволяет производить настройки элементов Kafka и соответственно использовать данный брокер на проекте с почти любым технологическим стеком.
Элементы инфраструктуры Кафки
Как было обозначено в определении, Kafka это именно система, а не просто атомарный брокер или очередь. Поэтому далее обозначим основные элементы инфраструктуры Kafka. И начнем мы с Zookeeper.
Zookeeper — это координирующий сервис, который используется в Apache Kafka для управления конфигурацией и хранения метаданных. Хоть далее подробно о нем говорится и не будет, но Зукипер сам по себе является очень важным элементом Кафки, так как если он упадет, то перестанет работать вообще вся Кафка. К счастью данное свойство не относится к другим элементам инфраструктуры.
Брокеры — это серверы в кластере Kafka, которые хранят данные и обрабатывают запросы на запись и чтение сообщений. Каждый брокер может принимать сообщения от продюсеров и отправлять их консюмеризм. Брокеры работают совместно, обеспечивая масштабируемость и отказоустойчивость системы.
Топики — это основные логические сущности в Kafka, представляющие собой категории или каналы для организации сообщений. Каждое сообщение, отправляемое в Kafka, публикуется в определённый топик, который может быть прочитан одним или несколькими потребителями.
Партиции — это подмножества топиков, которые позволяют распределять данные и обрабатывать их параллельно. Партиции как раз и выполняют функцию очередей в Кафке, так как в них хранится упорядоченное множество сообщений. Каждому топику может быть назначено несколько патриций, что увеличивает производительность и масштабируемость системы.
Оффсет — это уникальный идентификатор (аналог индекса в массиве), присваиваемый каждому сообщению в партиции. Он указывает на позицию сообщения и позволяет потребителям отслеживать, какие сообщения были прочитаны.
Продюсер — это клиентское приложение, которое отправляет сообщения в топики Kafka.
И наконец Консюмер — это клиентское приложение, которое читает сообщения из топиков Kafka. Консьюмеры могут быть организованы в группы, что позволяет им совместно обрабатывать сообщения и обеспечивать балансировку нагрузки. Важно отметить, что считанные оффсеты закрепляются именно за целой консюмер‑группой, а не отдельным консюмер, тем самым не позволяя двум консюмерам из одной группы считать одно и то же сообщение дважды.
Транспортный протокол общения Kafka
Далее предлагаю более подробно взглянуть на то, как в Кафке отправляются сообщения.
В качестве транспортного протокол общения Apache Kafka использует собственный бинарный протокол поверх TCP. Сообщение от Продюсера конвертируется через сериализатор в бинарный вид, а далее уже на стороне Консюмера сообщение возвращается в исходный формат через десиреализатор.
А какие же данные можно передавать через Кафку?
Через Кафку можно передавать любой тип данных, но делать этого не рекомендуется. Например, вместо pdf/mp3/jpeg файлов лучше использовать внутри json ссылки на их местонахождение в условном S3-хранилище. Так же стоит понимать, что Кафка не резиновая, поэтому рекомендуемый объем 1 сообщения в идеале не должен превышать 32кб.
Сообщение в Kafka (event)
Теперь давайте посмотрим что из себя представляет сообщение в Кафка.
Вообще в соответствии с официальной документацией каждое сообщение в Кафке на самом деле является событием. И структура у данного события следующая: ключ, значение, таймстемп и заголовок.
Разберем каждый элемент структуры поподробнее:
Key — используется для сегментации сообщений внутри партиции. Например, если нам нужно различать сообщения по типу операции, которая их породила, поскольку от этого зависит способ обработки сообщения консюмером. К примеру у нас есть доходный и расходные операции, и набор параметров в значении у них разный. Таким образом с помощью ключа мы заранее можем указать консюмеру как ему правильно распарсить полученный json.
Value — смысловая часть сообщения, по сути само сообщение как таковое. Это основной набор данных, который помещается в Кафку. И уже только с ним одним можно реализовать обмен сообщениями, не обращая внимания на другие элементы. В качестве значения сообщения отлично подойдет привычный json формат.
TimeStamp — время создания сообщения.
Headers — заголовки. Они чаще всего используется для указания вспомогательной информации такой как traceId конкретной операции или ссылки на приложение‑источник.
Отдельно отмечу, что сообщение в Кафка не удаляется после вычитки Консюмером, но оно может быть удалено в следующих 2 случаях:
1) По истечении срока жизни сообщения (TTL).
2) Или сообщение может быть удалено в результате переполненности топика данными. То есть, если в топик не влезает по объему данных новое сообщение, то для того чтобы оно все‑таки влезло будет удалено самое старое сообщение.
Как можно заметить вышеприведенная механика удаления сообщений порождает риски не обработки сообщения консюмером в результате его долгой работы. Делаем вывод — закладываем в конфигурации брокера время хранения сообщения и предел объема топика с запасом. А как это делать разберем как раз далее.
№2 ОСОБЕННОСТИ НАСТРОЕК КОНФИГУРАЦИИ
Настройки конфигурации Брокера Kafka
Дальше давайте поподробнее разберем параметры для конфигурации топика в Кафка, попутно смотря на пример их около‑универсального набора значений
default.replication.factor — количество реплик для каждого топика (в том числе и партиции). Данный параметр по сути задает степень отказоустойчивости данных для вашего топика.
min.insync.replicas — мин. количество реплик в синхронизированном состоянии. Этот параметр устанавливает минимального количество успешных репликаций данных для подтверждения записи сообщения, отправленного Продюсером. Этот параметр нужно ставить максимум на 1 меньше чем общее количество реплик (можно еще меньше делать данный параметр), иначе ваш брокер будет работать так, словно репликации в нем никакой нет, поскольку после падения хотя бы одного узла Кафка уже будет недоступна для записи — в таком случае весь толк от репликации теряется.
message.max.bytes — макс. разрешенный размер сообщения (в байтах)
num.partitions — количество партиций по умолчанию для новых топиков.
retention.ms — срок жизни сообщения в Кафка (TTL). Сообщение хранится не больше данного времени, после чего удаляется, причем стоит обратить внимание, что значение данного параметра задается в миллисекундах.
retention.bytes — макс. размер данных в топике. Суммарный вес всех сообщений в рамках одного топика не может превышать заданного значения, если при добавлении нового сообщения оно будет превышено, то самое раннее сообщение будет удалено из топика, чтобы появилось место для нового.
Настройки продюсера
Далее перейдем к разбору важных настроек продюсера..
batch.size — максимальный размер пакета данных в байтах. Он устанавливает какой максимальный суммарный объем сообщений в рамках одного батча может отправить Продюсер в Кафку.
linger.ms — максимальное время ожидания перед отправкой пачки сообщений
enable.idempotence (true) — позволяет сделать операцию Продюсера в сторону Кафка‑брокера атомарной транзакцией. То есть сделать так, что либо транзакция выполнится успешно, либо не будет выполнена вовсе, промежуточное состояние отсутствует.
max.in.flight.requests.per.connection — количество сообщений, которые могут быть отправлены до получения подтверждения от брокера. Установка этого параметра в 1 гарантирует, что сообщения будут отправляться по одному и в порядке их отправки, что поможет избежать дублирования и потери сообщений.
retries — количество попыток повторной отправки сообщения в случае неудачи. Установка значения больше 0 позволяет продюсеру повторно отправлять сообщения, если первоначальная попытка не удалась, что также может быть полезно для обеспечения надежности.
acks (acknowledges) — подтверждение отправки. О них далее поговорим подробнее.
Пример создания Продюсера на Python
Скрытый текст
from kafka import KafkaProducer
# Настройки продюсера
producer = KafkaProducer(
bootstrap_servers='localhost:9092', # Адрес Kafka брокера
acks='all', # Подтверждение от всех реплик
enable_idempotence=True, # Включение идемпотентности
max_in_flight_requests_per_connection=5, # Максимальное количество запросов до получения 1 подтверждения от брокера
retries=5, # Количество повторных попыток отправки сообщения
linger_ms=5, # Задержка перед отправкой сообщения (в миллисекундах)
batch_size=16384, # Размер пакета (в байтах)
key_serializer=str.encode, # Сериализация ключа
value_serializer=str.encode # Сериализация значения
)
# Пример отправки сообщения
producer.send('my_topic', key='key', value='value')
# Закрытие продюсера
producer.close()
Приведу принцип работы аксов
0 — Продюсер не ждет подтверждения от Брокера Кафки об успешном сохранении отправленных данных
1 — Продюсер ждет подтверждения от Брокера Кафки об успешном сохранении отправленных данных только в мастер‑партици.
-1 («all») — Продюсер ждет подтверждения от Брокера Кафки об успешном сохранении отправленных данных только во всех партициях (не только в мастер, но и в ее фолловерах).
По сути идея тут следующая: если вам нужно как можно больше и быстрее отправлять данные, не опасаясь за их целостность и гарантию доставки, то выбираем acks = 0. Если наоборот для нас в приоритет гарантия сохранности сообщений, то выбираем acks = -1.
Настройки консюмера
Теперь перейдем к важным настройкам конфигурации консюмера. Кафка исповедует принцип умного Потребителя, а это значит, что с Консюмером нужно быть предельно бдительным при его настройке.
group.id — идентификатор группы потребителей.
enable.auto.commit (false) — автоматическое подтверждение смещений (offsets) потребителем. При значении false Консюмер будет отсылать подтверждение об успешном прослушивании только в результате завершения всех своих действий с данными.
-
auto.offset.reset — с какого оффсета в топике начинать читать, если Консюмер подключается к топику впервые или если потеряна информация о его начальном оффсете чтения.
earliest — начинает читать с первого сообщения в топике. Это полезно, если необходимо обработать все сообщения, которые были опубликованы до момента запуска Консюмера.
latest — начинает читать с последнего сообщения в топике. Это значение по умолчанию и подходит для случаев, когда нужно обрабатывать только новые сообщения. Например, если вы хотите, чтобы ваш новый Консюмер начинал работать только с теми данными, что пришли в топик с момента его запуска.
none — Если нет доступных смещений, Консюмер выбросит исключение. Это значение используется реже, так как требует дополнительной обработки ошибок в коде. И от него есть смысл только в случае, если на стороне Консюмера есть вспомогательные фичи, которые предусматривают некий «ручной выбор» оффсета для чтения.
isolation.level (read_commited) — правило считывания операций
-
read_commited — Консюмер считывает только те сообщения от Продюсера, по которым завершилась транзакция (enable.idempotence = true у продюсера)
read_uncommited (default) — разрешает получение всех записей независимо от результата транзакции,
И в конце разберем важные временные конфигурации
heartbeat.intervals.ms — интервал подтверждения жизнеспособности потребителя. Временной цикл в рамках, которого потребитель подтверждает для Kafka свою активность.
max.poll.interval.ms — максимальное время в течение которого консюмер может не опрашивать кластер Kafka на наличие новых сообщений. Если Консюмер не вызывает poll() дольше указанного времени, то Консюмер будет считаться неработоспособным. Параметр нужно устанавливать больше чем прогнозируемое максимальное допустимое время обработки сообщения.
session.timeout.ms — максимальное время, в течение которого Консюмер может не подавать признаки жизни. Если консюмер не отправляет heartbeat в течение времени, указанного в session.timeout.ms, брокер считает его неработоспособным и запускает процесс ребалансировки — перераспределения партиций среди оставшихся активных консюмеров в группе.
В идеале временные параметры задать следующим образом:
heartbeat.intervals. < max.poll.interval.ms < session.timeout.ms
Тогда получится исключиться сценарии, при которых Консюмер по ошибке отключают от прослушивания сообщения.
Ниже приведу пример инициализации и настройки консюмера на Python
Скрытый текст
from kafka import KafkaConsumer
# Настройки консюмера
consumer = KafkaConsumer(
'my_topic', # Название топика
bootstrap_servers='localhost:9092', # Адрес Kafka брокера
group_id='your_group_id', # ID группы консюмеров
enable_auto_commit=False, # Отключение автоматического коммита
auto_offset_reset='earliest', # Начало с самой ранней доступной записи
isolation_level='read_committed', # Чтение только подтвержденных сообщений
heartbeat_interval_ms=3000, # Интервал между heartbeat-сообщениями (в миллисекундах)
max_poll_interval_ms=30000, # Максимальный интервал между вызовами poll (в миллисекундах)
session_timeout_ms=45000 # Тайм-аут сессии (в миллисекундах)
)
# Цикл для чтения сообщений
try:
while True:
# Получение сообщений из топика
messages = consumer.poll(timeout_ms=1000) # Установка таймаута для poll
for topic_partition, message_list in messages.items():
for message in message_list:
print(f"Received message: {message.value.decode('utf-8')}")
# Здесь можно добавить обработку сообщения
# Закрытие консюмера
finally:
consumer.close()
Консюмеры и Консюмер-группы
Также здесь стоит упомянуть еще одну особенность механики работы консюмеров Кафки. А именно, что консюмеры в рамках одной консюмеры группы не могут украсть у друг друга заявки. Это означает следующее: если первый консюмер прочитал или начал читать сообщение, то второй консюмер из той же группы не будет видеть это сообщение. Это полезно, когда у нашего сервиса‑потребителя имеется несколько инстансов. Тогда сам сервис — это консюмер группа, а его ноды — это консюмеры.
Важно: нужно вручную указывать id группы при прописывании конфигурации консюмера. Консюмер id же внутри группы для каждого участника может генерироваться самостоятельно Кафкой, но все равно его лучше тоже указывать вручную.
Тут же важное напоминание: оффсеты закреплены за консюмер группой, а не за отдельным консюмером.
Консюмеры и партиции
Еще один важный момент касаемо соотношения консюмеров и партиций. Хорошая практика — когда количество консюмеров в рамках 1 консюмер группы равно количеству партиций. Если партиций больше чем консюмеров, то потребители могут не поспевать за обработкой сообщений. Начнется отставание. И причем это отставание будет иметь эффект снежного кома: со временем твое отставание будет все больше и больше, пока это не приведет к перезатиранию сообщений или их удалению ввиду TTL.
А если консюмеров больше чем партиций, то лишние консюмеры будут просто оставаться без работы и ничего не читать.
№3 BEST-PRACTICES В РАБОТЕ С KAFKA
Теперь перейдем к разбору лучших практик в работе с Кафкой. И начнем с реализации семантики доставки сообщений.
3 семантики обработки данных
Всего существует 3 семантики обработки данных
At most once — сообщение будет обработано не более 1 раза.
Риск: сообщение не будет обработано вовсе.
Когда можно использовать: когда у нас данные, терпимые к потери, и в большом объеме. Например логирование действий пользователей в приложении.
At least once — сообщение будет обработана хотя бы 1 раз. Риск: считывание дублей.
Риск: дублирование обработки записей
Когда можно использовать: когда чтение записей для Консюмера реализовано идемпотентно. Например отправка Продюсером подтверждения Консюмеру о том, что какой‑то товар был доставлен на склад.
Exactly once — сообщение будет обработано строго 1 раз.
Риск: трудозатраты
Когда нужно использовать: когда у нас данные, для которых нужно обеспечить гарантию доставки, и при этом их считывание не идемпотентно. Например операции с платежными транзакциями.
Как реализовать Exactly once
Но раз Exactly once является по сути наилучшей практикой среди рассмотренных семантик, то как все‑таки его реализовать?
Есть несколько «фишечек», которые могут помочь с этим. Сперва начнем с вышеупомянутых конфигураций.
Настройки Продюсера
acks = -1 (all) — гарантированность сохранения сообщения продюсера в Топике Кафки.
enable.idempotence = true — транзакционность в операциях продюсера. Либо Продюсер будет гарантировано уведомлен об сохранении сообщения, либо сообщение не будет записано в топик вовсе.
Настройки на стороне Консюмера
isolation.level = read_commited — считываем только подтвержденные траназкции от продюсера
enable.auto.commit = false — Консюмер сам решает, когда сообщение можно признавать считанным
И далее приведу варианты достижения exactly once, распространенные среди авторитетных коллег нашего технологического цеха, которые активно используют Кафку на своих проектах.
Best practice-1. И так, начнем с идемпотентности обработки данных — предотвращение дублей на стороне консюмера. Например предварительная обработка сообщений на предмет их потвора: с помощью traceId в заголовке сообщения. Так в свою очередь делает команда Контур, подробнее об этом можно узнать в докладе Григория Кошелева.
Best practice-2. Фиксация оффсетов в отдельном хранилище. Это позволяет вынести мониторинг оффсетов с Кафки на сторону консюмера, тем самым оставив ответственность за выбор сообщения для считывания полностью на стороне Консюмера. Об этой практике публично рассказывала команда Рамблера, доклад Артема Выборнова.
Best practice-3. Реализация факультативного подтверждения считывания от Консюмера к Продюсеру. Это можно сделать 2 способами:
— либо создать еще один обратный топик Кафка
— либо реализовать синхронный «пинг метод».
С подобной практикой я встречался в ФинТехе при работе с частыми платежными операциями, которые обрабатываются асинхронно. При реализации дополнительного топика на подтверждение считывания Продюсер и Консюмер просто поменяются местами, где наш изначальный Консюмер будет уведомлять Продюсера о том, что сообщение с таким‑то id'шником успешно считано. А при дополнительном «пинг методе» по смыслу будет все ровно тоже самое, только уже с использование классического метода REST API в синхронном формате.
№4 КЕЙСЫ ПРИМЕНЕНИЯ KAFKA
Перейдем к актуальным кейсам применения Кафки на реальных проектах.
Потоковая обработка данных – сбор аналитики в реальном времени, обработка логов
Сперва разберем применение Кафки в рамках концепта, для которого она была непосредственно разработана в LinkedIn — потоковая обработка данных, являющихся логами действий пользователей в системе. Похожим образом, но только с небольшими модификациями, применяет Кафку, например, BMW.
Описание инфраструктуры
У БМВ есть сайт, на котором можно покупать автомобили и сопутствующие товары в виде запчастей и аксессуаров. Он является приложением‑продюсером.
Для обработки логов пользовательских действий и отслеживания состояния системы у БМВ есть система мониторинга на ELK стэке.
-
Для более развернутого анализа статистики у БМВ есть ML‑сервисы, такие как
NLP‑модель обработки естественного языка для анализа запросов в поддержку
ML‑модель для анализа потребительского выбора. Она отвечает на вопросы о том, какие товары чаще всего покупают вместе? Что можно предложить докупить клиенту в момент активной сессии? и тд.
Логи с информацией о пользовательских действиях вэб‑сайт как система продюсер отсылает в соответствующие топики Кафки, откуда уже ранее упомянутые приложения обрабатывают данные в режиме реального времени.
Кейс BMW иллюстрирует традиционный концепт использования Кафки с небольшими новшествами в виде моделей машинного обучения на стороне консюмеров.
Главное преимущество использования кафки в подобном кейсе заключается в том, что обмен данными производится по модели pub/sub, что дает возможность каждому из компонентов системы работать в своем режиме, не влияя на другой компонент. Kafka разделяет потоки данных на партиции, что позволяет обрабатывать разные части потока параллельно. Любое количество потребителей может подписаться на интересующую топики, не создавая при этом дополнительных нагрузок для приложения‑продюсера.
Кросс-сервисное взаимодействие между несколькими приложениями-производителями и -потребителями
Кафка отлично подходит для реализации взаимодействия сразу между несколькими приложениями/сервисами. Данное свойство Кафки можно проиллюстрировать на следующем примере. Допустим, у нас есть абстрактный IT ландшафт, состоящий из 6 систем:
интернет банкинг
мобильный банкинг
сервис обработки платежей
сервис рекомендаций
аналитическая система
Сервис мониторинга и оповещения
Всем этим сервисами необходимо взаимодействовать с друг другом, к примеру:
Сервис рекомендаций хочет собирать информацию о пользовательских действиях в интернет‑банке.
Интернет и мобильный банкинг хотят публиковать запросы на проведение транзакций для сервиса обработки платежей.
Сервис мониторинга хочет следить за транзакциями и состояниями платежей. А аналитическая система хочет собирать информацию о всем пользовательском опыте в рамках различных операций.
И в итоге мы получаем систему с как минимум 4 различными программными интерфейсами, взаимодействие внутри которой выглядит как паутина.
Соответственно все это подталкивает нас к необходимости систематизации потоков данных. И теперь давайте взглянем на то, как будет выглядеть наш ландшафт при внедрении Кафки.
У нас есть 2 продюсера, 3 консюмера и сервис платежей, который выполняет сразу обе функции.
Через топик user‑actions: интернет банкинг публикует информацию о действиях пользователя в системе, откуда сервис рекомендаций и аналитическая система считывают данные.
Через топик transactions каждый банкинг публикует запросы на переводы, а сервис обработки платежей и аналитическая система считывают информацию
И наконец в топик payment status процессинговый сервис пишет информацию о завершенных транзакций в целях сбора данных аналитической системой и уведомления сервиса мониторинга о результатах платежных операций.
В итоге применение Кафки позволило нам консолидировать потоки данных и снизить количество дополнительных интеграций за счет использования программного интерфейса Кафки из коробки.
Основа для шины данных
Теперь когда мы знаем, что Кафка отлично подходит на должность точки агрегации потоков данных в системе, то можем рассмотреть следующий кейс, который будет являться логическом продолжением предыдущего, а именно использование Кафки в качестве основы для разработки корпоративной шины данных.
Как раз подобное было реализовано командой OzonTech.
Решение от Ozon обладает следующими особенностями:
В общей экосистеме более 3500 микросервисов и всем им нужно обмениваться между собой данными.
Аналогично первому кейсу у нас имеется большой объем потоковых данных, который подлежит обработке в реальном времени.
В центре всего решения стоит Кафка.
-
Но поверх Кафки также реализован дополнительный слой абстракции на Golang, который устанавливает со всеми сервисами системы постоянное соединение по gRPC.
Дополнительный слой необходим не только для «проксирования» сообщения между приложениями и Кафкой, но и для предобработки этих сообщений.
Также благодаря gRPC streaming Шина устанавливает постоянное соединение с Производителями и Потребителями, которое аналогично асинхронному формату общения pub/sub.
В итоге подобное решение способно работать со следующими нагрузками:
2,5 гб/ в сек на запись и 7 гб/сек на чтение
Рейт запросов: 1,5 млн rps на запись и 2 млн rps на чтение
Инстансы приложений: 50к продюсеров и 16к консюмеров
Вот это теперь по‑настоящему иллюстрирует то, что значит высоконагруженное и масштабируемое решение, когда мы говорим о Кафке.
Асинхронное взаимодействие между системами
Также Кафку часто используют для реализации асинхронного взаимодействия в случае, если есть клиентское приложение, для которого важна скорость ответа, и мастер‑система, которая реализует препроцессинг данных, проводит сложные транзакции в БД и ходит попутно в другие системы, чтобы отдать ответ клиентскому приложению. И поскольку у нас под капотом одного запроса лежит множество косвенных процессов, то callback приходит не скоро, а в рамках работы с клиентским приложением, допустим, есть нефункциональное требование на лимит времени ответа API.
Поэтому чтобы не обременять пользователя ожиданием обработки запроса и «вечной загрузкой на экране», можно производить исполнение метода на создание записи в асинхронном формате. При использовании Кафки запрос на создание записи от клиентского приложения к мастер‑системе все также остался, но теперь вместо того, чтобы ждать его полного завершения мы сразу отвечаем клиентскому о том, что ваша заявка на исполнение запроса принята, ожидайте результата выполнения запроса в Кафке. Соответственно мы на стороне мастер‑системы, не беспокоясь о времени выполнения, проводим все необходимые процедуры для выполнения запроса, после чего кладем сообщение в Кафку с результатом записи. Клиентское приложение считывает сообщение и уведомляет пользователей пушами об итогах выполнения запроса.
Когда не стоит применять Kafka
Справедливо будет упомянуть о случаях, когда Кафку применять не стоит. Несмотря на все дифирамбы в сторону данной технологии, Кафка это далеко не панацея и есть кейсы, где ее применение, мягко говоря не рекомендуется, так как для этого есть более удачные инструменты.
Не надо № 1. Например, если нужно реализовать очереди с приоритетами. Кафка под очереди с приоритетами не заточена, в ней из коробки реализовать можно только FIFO. Поэтому для решения подобной задачи лучше подойдет Rabbit, поскольку в нем данный функционал имеется изначально.
Не надо № 2. Второй кейс это, если требуется реализовать функционал очереди в рамках одного приложения. Безусловно одно и то же приложение может одновременно выступать для себя консюмером и продюсером. Но причины, по котором так лучше не делать следующие:
Кафка была разработана для асинхронного взаимодействия именно 2 разных приложений/систем.
И второе это будет излишнее усложнение решения за счет внедрения дополнительной интеграцией. А подобный оверинжиниринг потом может обернуться трудностями в поддержке решения.
Поэтому последнее, что я бы тут добавил, так это: «если у вас есть самолет, то на нем нужно летать в небе, а не кататься по шоссе. Ведь для проезжей части лучше подойдут привычные автомобили» — думаю, аллегория вполне понятная ?
Заключение
На этом статья заканчивается. Благодарю за прочтение. Если в ходе ознакомления с материалом появилось желание рассказать о своих кейсах и нюансах использования Кафки, то милости прошу в комментарии)
До новых встреч, Хабр!
scarab
Тут можно упомянуть , что кафка уже давно умеет работать по KRAFT без зукипера.