Академический Документы
Профессиональный Документы
Культура Документы
https://www.youtube.com/watch?v=-AZOi3kP9Js&ab_channel=%D0%92%D0%BB%D0%B0%D0%B4%D0%B8%D0%BC
%D0%B8%D1%80%D0%91%D0%BE%D0%B3%D0%B4%D0%B0%D0%BD%D0%BE%D0%B2%D1%81%D0%BA
%D0%B8%D0%B9
https://www.youtube.com/watch?v=PtFTGQG2LwA&ab_channel=InterviewDone%21
Возможное решение:
Нужно взять посредника (broker). Он примет на себя функцию получения данных от producer’ов и
предоставление данных consumer’ам. В брокере есть набор ящиков под каждый тип событий.
Продьюсеры складывают в эти ящики сообщения, а консьюмеры потом забирают нужные им
события из разных ящиков.
Удобства:
- надежность и гарантия доставки
Гарантию обеспечивает единый узел – брокер.
- подключение новых пользователей
- отправители не знают получателей
- техническая поддержка
- интеграция разных стеков происходит просто
Продьюсерам и консьюмерам нужно знать только API брокера
- Масштабирование
- Репликация (для надежности)
Kafka Message
Topic/Partition
Topic – стрим данных. Внутри очередь, в которую складываются все входящие сообщения.
В очередь сообщения попадают в том же порядке, в котором отправляются producer’ом. И
считываем в том же порядке. Фича Kafka – обеспечение FIFO (ordering support).
При чтении (консьюмером) сообщения/события из топика не пропадают, данные не удаляются =>
широковещательный режим: может быть несколько консьюмеров, которые потребляют одни и те
же даные. Здесь показана линейная работа, в один поток, не очень работает при больших нагрузках.
Partitions
Разделение единой очереди на партиции. Это конфигурируемый параметр (мы его задаем). Можем
отправлять не одним продьюсером, а сразу несколькими, и также получать – можем не одним
последовательным консьюмером, а сразу в паралелль из разных партиций получать однотипные
события.
Консьюмер потребляет не в том порядке, в котором изначально были заложены события у
продьюсера, но они в том порядке, в котором лежат в партиции. Упорядочивание – на уровне
партиций.
Topic – труба
Partition – трубочки
Broker – место, где проходят трубочки
В проде лучше не делать число брокеров меньше, чем 3 (для устойчивости и надежности системы)
- файл 000000000000.log
- файл 000000000000.index
Это маппинг offset на position.
- файл 000000000000.timeindex
В итоге: по timestamp получаем offset в *.timeindex file => в файле *.index по offset получаем
position => по position в *.log file получаем наше сообщение.
Message Key (Key – часть сообщения, может быть null; в качестве key можно использовать id
пользователя)
- Если null, то выбирается partition по round robin (по очереди, по кругу; все партиции равномерно
заполняются)
- Если не null, то все сообщения с этим ключом пишутся всегдюа в один partition (случай, когда
события от одного пользователя нужно обрабатывать по очереди – events одного клиента, лог
одного сервиса, изменения из одного источника…)
Подводный камень – timestamp сегмента (может быть кем-то установлен очень большим (далеким)
и сегмент не будет удаляться).
Если бы на каждом брокере было только по одной партиции из топика, то при «падении» брокера,
данные бы пропадали.
Нюансы:
- Отставание данных в репликах от ГЛАВНОЙ (Leader) реплики
Отставание данных
!!! Всегда пишется в Leader-реплику.
Каким образом данные попадают в follower-реплики?
Фолловеры опрашивают лидера – «Что нового есть? Дай нам.» Так как это действие
периодическое, то может быть отставание по событиям.
Что произойдет, если Leader «выпал»? Кто теперь Leader? И все ли данные у него есть? =>
Ненадежно! (может оказаться, что ни у одного фоловера нету всех данных)
Решение:
В Кафке есть in-sync реплики – ISR (min.insync.replicas = 3, ставят обычно на единицу меньше, чем
кол-во всех реплик).
Когда пишем события в Leader, после записи, лидер реплика обращается к ISR фоловерам и
синхронно записывает события в эти фоловеры. Оставшиеся не ISR фоловеры работают в прежнем
режиме и данные могут отставать.
1. Операция fetch metadata из Zookeeper (в новых версиях Kafka – producer идет в broker, а broker в
Zookeepr)
Producer нужно знать из чего состоит кластер. Дальше ему нужно отправлять сообщения в топик,
здесь нужно знать, где находятся leader реплики.
Нюанс:
Сама операция send заявлена как асинхронная. Но fetch metadata блокирующая операция, значит
если, например, с Zookeper что-то случилось и мы зависли, и этот таймаут может длиться 60 сек
(настраиваемый параметр). Это тяжелая операция.
Таким образом, не нужно на каждое событие создавать нового продьюсера. Нужно создать одного
продьюсера и с ним работать. Он один раз подгрузит мета данные, закэширует, будет с ними
работать и периодически (или по изменениям) обновлять.
2. serialize message
Полученные сообщения (байты) сериализуем в нужный формат.
Указываем:
- key.serializer
- value.serilizer
(например, StringSerializer)
3. define partition
Выбор партиции, в которую пойдет сообщение.
Варианты:
explicit partition (указать номер партиции)
round-robin («разберись сам»)
key-defined (key_hash % n (остаток от деления)) – очеь часто используемый
Эту фичу задействуем, когда хотим обеспечить строгую согласованность между операциями
обновления записи (update) и чтения (get) по пользователю. Все события одного пользователя
кладем в одну партицию с помощью того, что в качестве ключа передаем идентификатор
пользователя (key). А в партиции Kafka гарантируем упорядоченность – в каком порядке положили,
в таком порядке потом обработали.
4. compress message
Используем настройки compression.codec
5. accumulate batch
Аккумулируем батч для повышения производительности
batch.size – копим батч до достижения batch.size (16 килобайт по-умолчанию)
Пример, накопили 10 сообщений. Если они привысили 16 килобайт, и батч может быть
отправлен в брокер.
linger.ms – если не можем накопить батч, но хотим отправить сообщения, то действуем
по тайм-ауту.
Нюанс: может быть такое, что не batch.size, ни linger.ms не превышен, но в тоже время батч
отправляем в брокер. Допусти видим, что у брокера номер 2 две партиции 1 и 2, и создаем два batch
для 1-ой партиции и для 2-ой. Если суммарный оьббъем двух батчей превышает batch.size, то эти
бати отправляются, несмотря на то, что они не заверщены. Отправляются, так как идут на один
брокер.
Consumer
Сообщения из partition читаются в порядке добавления, FIFO.
Данные из разных partition могут перемешиваться любым способом.
1. fetch metadata
get:
- cluster state
- topic placement
Где находятся топики, какие реплики являются leader, на каких они лежат брокерах, состояние
кластера?
Подключение к Leader-репликам
всех партиций топика.
Как решить?
В Kafka есть фича – хранение оффсетов и коммиты. Есть специальный системный topic -
__consumer_offsets.
В топике __consumer_offsets хранятся оффсеты для каждой группы, которые уже прочитали.
Теперь если после обработки первых 3-х сообщений консьюмер упал, то происходит переключение
обработки текущей партиции на другого консьюмера. Который в свою очередь из оффсет-топика
считывает информацию, где он видит какая партиция обрабатывалась и какой был последний
коммит (offset = 2). Теперь запрашивает у партиции сообщения с оффсетом 3. Таким образом
обработка сообщений не дублируется.
Kafka сохраняет offsert для каждого consumer group для каждого partition – указатель на то, какое
сообщение читать дальше.
Каждый брокер – bootstrap сервер. Каждыц брокер знает обо всех других брокерах, их топиках и
партициях.
Подводя итоги:
Пока количество partitions остается постоянным, один и тот же ключ пишется в один и тот же
partition. При удалении, а затем добавлении партиций, происходит перебалансировка по свои
алгоритмам и key уже станет другим.