Apache Kafka — это платформа для потоковой обработки данных, которая поддерживается сообществом Apache Software Foundation. Kafka была создана как распределённая система обмена сообщениями, однако в настоящее время она используется для обработки данных в различных предметных областях.
"Не бойтесь файловой системы!" - так начинается раздел о персистентности в официальной документации Kafka.
Как работает Apache Kafka
Apache Kafka представляет собой брокер сообщений и хранит данные в виде потоков (streams) или топов (topics). Потоки хранят данные в хронологическом порядке, а топики позволяют выбирать сообщения по различным критериям.
При отправке сообщения в Kafka оно сохраняется в топике и получает идентификатор, который позволяет определить его место в потоке. Уникальность идентификатора обеспечивается благодаря использованию конвергенции (convergence), которая используется в системе для предотвращения потери данных при отказе узлов. Конвергенция гарантирует, что все узлы в кластере Kafka используют одни и те же данные.
Пример конвергенции:
- Все узлы в Kafka работают над одним и тем же потоком данных.
- Один из узлов отправляет сообщение в топик.
- Сообщение отправляется всем узлам в кластере.
- Узлы конвергируют данные, обновляя свои копии топика.
- Узлы-получатели запрашивают данные из топика и получают сообщение, отправленное узлом-отправителем.
Данные в топиках хранятся в формате JSON или Avro. Данные в топиках могут быть восстановлены после сбоя или перезапуска кластера Kafka, так как конвергенция обеспечивает согласованность данных на каждом узле в кластере. По умолчанию конвергенция происходит каждые 100 миллисекунд, но этот интервал можно настроить в зависимости от потребностей.
Как данные записываются
Процесс записи данных в Kafka включает в себя отправку сообщений в брокер сообщений, который затем сохраняет их в топике.
Процесс персистенции Kafka
Наиболее эффективным способом использования вращающихся дисков является линейная запись, потому что она довольно быстрая и имеет высокую пропускную способность благодаря предсказуемому поведению и сильной оптимизации со стороны ОС.
Это звучит странно, но последовательный доступ к диску может быть быстрее, чем случайный доступ к памяти!
Твердотельные диски, в свою очередь, имеют гораздо более высокую скорость случайного чтения и записи по сравнению с жесткими дисками и могут использоваться еще более эффективно.
Большинство современных операционных систем широко используют память для дискового кэширования.
Например, страничный кэш или дисковый кэш в Linux - это механизм, ускоряющий доступ к файлам на энергонезависимых носителях.
- Linux хранит данные в свободных областях памяти и использует их в качестве кэша, а все чтения и записи на диск проходят через этот канал.
- Подход страничного кэша в Linux называется кэшем с обратной записью. Если данные записываются, то сначала они записываются в страничный кэш и помечаются как грязные.
- Грязные страницы означают, что эти данные сохраняются в страничном кэше, но также должны быть сохранены на базовом устройстве хранения.
- Грязные страницы периодически переносятся в хранилище. Таким образом, негрязные страницы имеют идентичные копии в базовом хранилище, а грязные - нет.
- Файловые блоки записываются в страничные кэши не только при записи, но и при чтении файлов.
- Если вы попытаетесь прочитать файл дважды, выполняя одно чтение за другим, то второе чтение должно быть намного быстрее, поскольку оно читается непосредственно из страничного кэша и не требует дискового ввода-вывода.
Apache Kafka выигрывает от Page Cache, поскольку его можно рассматривать как реализацию кэша в памяти "free-to-use", предоставляемую операционной системой, которая без особых усилий позволяет использовать все преимущества кэширования при нулевых затратах.
Но было бы неправильно сказать, что Kafka вообще не полагается на файловую систему.
На данный момент эта ситуация может показаться запутанной, поскольку мы не можем точно определить, когда Kafka сохраняет данные на диски.
Если вы помните параметр producer.acks, то, вероятно, вы знакомы с политикой подтверждения в Kafka. Брокер вернет ack производителю после того, как необходимое количество реплик сохранит это изменение.
Но поскольку мы уже знакомы со страничным кэшем, означает ли это, что изменение будет записано в память и может подвергнуться риску потери?
Эти предположения, по крайней мере, частично справедливы. Так, если все реплики одновременно выйдут из строя, то даже при использовании acks=all вы все равно можете потерять обновление, поскольку кэш страниц не успеет перенести изменения в базовое хранилище.
Кластер Kafka может допустить отказ N-1 узлов. Это означает, что хотя бы одна реплика из списка синхронизированных реплик должна остаться в живых.
Сценарий, когда все реплики выйдут из строя в один и тот же момент и не успеют выполнить свои IO, кажется очень маловероятным.
Отправка сообщений
Отправка сообщений в Kafka может быть выполнена с помощью различных инструментов и языков программирования. Например, в Python можно использовать библиотеку kafka-python для отправки сообщений.
1 2 3 4 5 6 7 8 | import kafka #Создание клиента Kafka client = kafka.KafkaClient() #Отправка сообщения producer = client.produce('my_topic', value=json.dumps({'message': 'Hello Kafka!'}), bootstrap_servers='localhost:9092') producer.close() |
Для отправки сообщений в других языках программирования можно использовать аналогичные библиотеки.
Заключение
Kafka в значительной степени полагается на персистентность, предоставляемую операционными системами.
Большинство из них способны использовать так называемые страничные кэши, действующие как кэши с обратной записью. Эта оптимизация дает Kafka удобное и быстрое кэширование в памяти бесплатно.
Когда обновление записывается на узел Kafka, в большинстве случаев это означает, что изменение записано в страничный кэш, но, возможно, еще не записано в базовое хранилище.
Это делает Kafka еще быстрее, поскольку при записи не используется синхронный ввод-вывод, но, с другой стороны, несколько снижает отказоустойчивость.