July 13, 2021

Apache Kafka

Какое-то время назад начал знакомиться с кафкой для рабочих задач – мы выбирали тулзу для хранения данных по транзакциям из WAL постгреса. Наши сисадмины очень не любят JVM, так что взяли API-совместимую штуку на плюсах – RedPanda. Так как апи совместимое, теория справедливая для кафки, справедлива и для панды.

  • кафка нужна для долговременного хранения логов. Она не выступает как брокер очереди, а служит для хранения ивентов.
  • сообщение, которое мы шлем в кафку состоит из номера партиции, ключа, значения и заголовков. По дефолту номер партиции вычисляется по ключу (хэш). У продюсера есть возможность настроить отправку сообщений батчами, а не по одному. Есть настройки, которые задают размер батча, либо дельту, через которую сообщения улетают
  • в отличие от очереди, из которой консьюмер вычитывает и обрабатывает сообщение, тут мы работает с оффсетами. Каждый консьюмер читает с нужных ему мест
  • таким образом мы как-будто бы храним лог вечно (есть настройки по очищению старых записей, например по возрасту или по количеству занимаемого места)
  • кафка рассовывает входящие сообщения по топикам. Это логическое разделение хранимых логов. Сообщения в одном топике должны иметь одинаковый формат, чтобы не ломать совместимость с консьюмерами
  • топики содержат партиции. Это порции, разделяющие непосредственно записи логов. Они нужны для обеспечения конкурентного доступа из консьюмеров, а также для отказоустойчивости – разные партиции одного топика могут жить на разных нодах + они участвуют в репликации данных по нодам
  • при записи в разные партиции данных, относящихся к одному событию, мы рискуем потерять порядок – из-за асинхронной считки из разных партиций. На помощь приходит ключ партиционирования, который позволяет определять однотипные ивенты в нужную партицию. В качестве ключа лучше всего брать какой-нибудь UUID объекта
  • группы консьюмеров – консьюмеры могут быть объединены в группу, которая будет читать конкретный топик с партициями. Принадлежность к группе будет определяться по group.id. Логично, что оптимальное число консьюмеров в группе должно быть раво количеству партиций в читаемом топике – тогда будет эффективная паралеллизация чтения
  • нельзя дропать ивенты. Ивент – это то, что уже произошло и то, на что мы обязаны среагировать . Например, мы принимаем заказ на покупку. В момент получения запроса на стороне веб-приложения, мы имеем дело с некой командой – что-то, что только готовится к обработке. На этом этапе мы можем сделать какие-то валидации и дропнуть дальнейшую обработку, если обнаружим ошибку. В случае, если мы производим валидацию уже после создания и отправки события, мы получаем проблему, так как ивент, который мы обрабатываем, считается уже свершившимся. Если мы дропнем сообщение из кафки, мы рискуем нарушить целостность данных, незавершить пользовательскую сторю (покупатель не получит товар) и другие неприятности
  • оффсеты - есть два типа оффсетов: текущий и закомиченный. Текущий служит информацией для кафки о том, сколько записей было выдано консьюмеру. Закомиченный оффсет говорит о том, сколько записей консьмеры подтвердили и обработали. Второй тип служит для балансировки, на случай, если какая-то из партиций будет перебалансирована
  • есть два типа коммитов оффсета - автоматический и ручной. Автоматический включен по дефолту и коммитит последний оффсет 5 раз в секунд (настраиваемо). Но с ним возможна проблема, когда обработка происходит быстрее 5 секунд и оффсет меняется раньше коммита. Тогда коммит проебется. Ручной коммит также делится синхронный и асинхронный. Тут все просто, единственное что - лучше делать синхронный коммит при закрытии соединения.
  • гарантия надежности (акки) - есть три вида акноледжей. Первый просто сигналит о том, что сообщение дошло до брокера по сети. Второй проверяет, что сообщение записано в ведущий брокер. Третие убеждается в том, что сообщение реплицировано по остальным брокерам (at least once, если брокеров больше 2)