Kafka Streams и ksqlDB: данные в реальном времени - Митч Сеймур - E-Book

Kafka Streams и ksqlDB: данные в реальном времени E-Book

Митч Сеймур

0,0

Beschreibung

Работа с неограниченными и быстрыми потоками данных всегда была сложной задачей. Но Kafka Streams и ksqlDB позволяют легко и просто создавать приложения потоковой обработки. Из книги специалисты по обработке данных узнают, как с помощью этих инструментов создавать масштабируемые приложения потоковой обработки, перемещающие, обогащающие и преобразующие большие объемы данных в режиме реального времени. Митч Сеймур, инженер службы обработки данных в Mailchimp, объясняет важные понятия потоковой обработки на примере нескольких любопытных бизнес-задач. Он рассказывает о достоинствах Kafka Streams и ksqlDB, чтобы помочь вам выбрать наиболее подходящий инструмент для каждого уникального проекта потоковой обработки. Для разработчиков, не пишущих код на Java, особенно ценным будет материал, посвященный ksqlDB.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 455

Veröffentlichungsjahr: 2024

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Митч Сеймур
Kafka Streams и ksqlDB: данные в реальном времени
2023

Переводчики Л. Киселева, И. Рузмайкина

Митч Сеймур

Kafka Streams и ksqlDB: данные в реальном времени. — СПб.: Питер, 2023.

ISBN 978-5-4461-3945-3

© ООО Издательство "Питер", 2023

Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.

Предисловие

Бизнес все чаще строится вокруг событий — информации о происходящем в компании, поступающей в режиме реального времени. Но какую именно инфраструктуру можно считать подходящей для полного использования информации о событиях? Это вопрос, над которым я задумался в 2009 году, запустив проект Apache Kafka в LinkedIn. В 2014 году я стал соучредителем Confluent, чтобы дать окончательный ответ на него. Платформе потоковой передачи событий требуется не только возможность хранения и доступа к дискретным событиям, но также механизм подключения к множеству внешних систем и поддержка глобального управления схемами, метриками и мониторингом. А самой важной, пожалуй, является поддержка потоковой обработки — возможность выполнения непрерывных вычислений с бесконечными потоками данных. Без этого платформа потоковой передачи событий просто неполноценна.

Сейчас как никогда потоковая обработка играет ключевую роль во взаимодействиях компаний с внешним миром. В 2011 году Марк Андриссен (Marc Andreessen) написал статью под названием Why Software Is Eating the World. Основная ее идея: любой процесс, который можно воплотить в программе, в конечном счете будет воплощен. Марк оказался провидцем: программное обеспечение проникло во все мыслимые сферы.

Но менее очевидный и более важный результат — успех бизнеса стал все больше зависеть от программного обеспечения. Другими словами, ключевые процессы, образующие основу бизнеса — от создания продукта до взаимодействия с клиентами и предоставления услуг, — все чаще определяются, отслеживаются и выполняются с помощью программного обеспечения. Что изменилось благодаря внедрению этой всеобъемлющей динамики? То, что программное обеспечение в таком новом мире вряд ли будет напрямую взаимодействовать с человеком. Более вероятно, что оно будет программно запускать действия или реагировать на действия других частей программного обеспечения, которые непосредственно поддерживают бизнес.

Возникает вопрос: насколько хорошо подходят для этого развивающегося мира традиционные архитектуры приложений, основанные на существующих базах данных? Практически все базы данных, от самых известных реляционных баз данных до новейших хранилищ пар «ключ — значение», следуют парадигме пассивного хранения информации, когда база данных ожидает команд, чтобы что-то извлечь или изменить. Эта парадигма ориентирована на приложения, взаимодействующие с человеком: пользователь осуществляет мониторинг интерфейса и инициирует действия, которые преобразуются в запросы к базе данных. Думается, что это только половина проблемы хранения данных. Пассивная составляющая хранения должна быть дополнена способностью реагировать на события и обрабатывать их.

События и потоковая обработка — ключ к успеху в этом новом мире. События образуют непрерывный поток данных в бизнесе, а потоковая обработка автоматически выполняет код в ответ на изменения на любом уровне детализации, делая это в соответствии с накопленной информацией обо всех изменениях, которые произошли до этого. Современные системы потоковой обработки, такие как Kafka Streams и ksqlDB, упрощают создание приложений для мира, говорящего на языке программного обеспечения.

В своей книге Митч Сеймур (Mitch Seymour) доходчиво описывает эти передовые системы, исходя из фундаментальных принципов. Он рассматривает понятия, лежащие в их основе, подробно объясняет нюансы работы каждой системы и приводит практические примеры использования потоковой обработки в реальном мире. Важность этой парадигмы программирования трудно переоценить, и освоение Kafka Streams и ksqlDB помогут добиться успеха в ее применении на практике.

Джей Крепс (Jay Kreps), один из создателей Apache Kafka, сооснователь и генеральный директор Confluent

Введение

Инженеры и специалисты в области обработки данных, к которым я отношу и себя, никогда не испытывали недостатка в технологиях, конкурирующих за наше внимание. Что бы мы ни делали: просматривали любимые форумы или презентации на технических конференциях, выискивали новости информационных технологий или читали технические блоги — всегда найдется много достойного нашего внимания, порой голова идет кругом.

Но, найдя тихий уголок, отодвинув весь шум на задний план и поразмыслив не спеша, мы начинаем отличать закономерности от белого шума. Мы живем в эпоху взрывного роста объемов данных, и в помощь нам для их обработки в больших масштабах создано множество технологий. Нам говорят, что это современные решения для современных проблем, мы сидим и обсуждаем «большие данные», как будто это авангардная идея, хотя на самом деле сосредоточенность на их объеме — это только половина дела.

Технологии, решающие одну лишь проблему большого объема данных, как правило, основаны на методах пакетной обработки данных. Они предполагают применение задания к некоторому массиву данных, накопившихся за определенный период времени. В каком-то смысле это похоже на попытку выпить океан одним глотком. С существующими вычислительными мощностями и парадигмами некоторым технологиям действительно удается добиться желаемого, хотя и за счет длительной задержки.

Однако современные данные обладают еще одним свойством, на котором мы сосредоточимся в книге: они перемещаются по сетям устойчивыми и бесконечными потоками. Технологии, которые мы рассмотрим далее, — Kafka Streams и ksqlDB — специально созданы для обработки таких непрерывных потоков данных в режиме реального времени. Они обеспечивают огромные конкурентные преимущества по сравнению с разнообразными технологиями, пытающимися «выпить океан». В конце концов, многие бизнес-задачи зависят от времени, и если требуется обрабатывать и преобразовывать данные по мере их поступления, то Kafka Streams и ksqlDB помогут организовать это легко и эффективно.

Изучение Kafka Streams и ksqlDB — еще и отличный способ познакомиться с более широкими понятиями, связанными с обработкой потоков данных, включая моделирование данных разными способами (в форме потоков и таблиц), применение преобразований без сохранения состояния, использование локального состояния для сложных операций (соединения, агрегирования), представление различных семантик времени, группировка данных во временные сегменты/окна и многое другое. Другими словами, знание Kafka Streams и ksqlDB поможет вам различать и оценивать решения обработки потоков, которые существуют в настоящее время и могут появиться в будущем.

Я рад поделиться с вами своими знаниями этих технологий, повлиявших на мою карьеру и помогавших мне в свое время решать технологические задачи, которые, как тогда казалось, были мне не по зубам. Фактически к тому моменту, когда вы закончите читать это предложение, одно из моих приложений Kafka Streams успеет обработать девять миллионов событий. Чувство, которое вы испытаете, оснастив бизнес реальной ценностью и не потратив много времени на поиск решения, вдохновит вас на длительную и основательную работу с этими технологиями. А краткие и выразительные языковые конструкции сделают процесс создания этих ценностей похожим больше на искусство, чем на рутинный труд.

И так же, как в любом другом виде искусства, человеческая природа требует поделиться своими достижениями. Поэтому можете считать эту книгу сборником моих любимых произведений из области потоковой обработки: Kafka Streams и ksqlDB, том 1.

Кому адресована книга

Эта книга адресована специалистам по обработке данных, желающим научиться создавать масштабируемые приложения потоковой обработки для перемещения и преобразования больших объемов данных в режиме реального времени. Подобные умения часто необходимы для поддержки интеллектуальной обработки данных, аналитических конвейеров, обнаружения угроз, обработки событий и многого другого. Специалисты по данным и аналитики, занимающие­ся анализом потоков данных в реальном режиме времени и желающие усовершенствовать свои навыки, тоже смогут почерпнуть немало полезного из этой книги. В ней автору удалось отойти от привычной пакетной обработки, которая обычно доминировала в этих областях. Предварительный опыт работы с Apache Kafka не требуется, хотя некоторое знакомство с языком программирования Java облегчит знакомство с Kafka Streams.

Структура издания

Эта книга состоит из 12 глав.

• Глава 1 содержит введение в Kafka и инструкции по запуску кластера Kafka с одним узлом.

• Глава 2 содержит введение в Kafka Streams, начиная с общих сведений и обзора архитектуры и до описания шагов по запуску простого приложения Kafka Streams.

• Главы 3 и 4 посвящены обсуждению операторов высокоуровневого предметно-ориентированного языка (Domain-Specific Language, DSL) Kafka Streams с сохранением и без сохранения состояния. Обе главы наглядно демонстрируют использование этих операторов для решения интересной бизнес-задачи.

• Глава 5 содержит обсуждение роли времени в приложениях потоковой обработки данных, она демонстрирует, как использовать окна для выполнения сложных операций с сохранением состояния, включая оконные операции соединения и агрегирования данных. Здесь показаны ключевые концепции на примере прогностической медицины.

• Глава 6 описывает внутренние особенности механизмов обработки с сохранением состояния, а также дает некоторые советы по работе с приложениями Kafka Streams с состоянием.

• Глава 7 посвящена низкоуровневому программному интерфейсу Kafka Streams Processor API, который можно использовать для планирования периодических функций, а также для доступа к состоянию приложения и метаданным записей. Эта глава основана на примерах использования Интернета вещей (Internet of Things, IoT).

• Глава 8 содержит введение в ksqlDB и обсуждает историю и архитектуру этой технологии. Здесь вы увидите, как установить и запустить экземпляр сервера ksqlDB, и познакомитесь с некоторыми приемами работы с интерфейсом командной строки ksqlDB.

• Глава 9 описывает функции интеграции данных ksqlDB, поддерживаемые в Kafka Connect.

• Главы 10 и 11 подробно рассматривают диалект ksqlDB SQL, демонстрируют приемы работы с различными типами коллекций, выполнения запросов на передачу и извлечение данных и многое другое. Представление идей в этой главе будет основано на сценарии использования Netflix: отслеживании изменений в различных шоу и фильмах и предоставлении доступа к этим изменениям другим приложениям.

• Глава 12 представляет сведения, необходимые для развертывания приложений Kafka Streams и ksqlDB в реальных условиях. Здесь же вы узнаете о мониторинге, тестировании и контейнеризации приложений.

Исходный код

Исходный код примеров для этой книги доступен на GitHub: https://github.com/mitch-seymour/mastering-kafka-streams-and-ksqldb.

Инструкции по сборке и запуску каждого примера вы найдете в том же репозитории.

Версия Kafka Streams

На момент написания книги последней была версия Kafka Streams 2.7.0. Именно она используется здесь, хотя многие примеры будут работать и с более старыми или новыми версиями библиотеки Kafka Streams. Будут приложены все силы, чтобы своевременно обновлять исходный код при включении критических изменений в новые версии библиотеки и размещать эти обновления в отдельной ветке (например, kafka-streams-2.8).

Версия ksqlDB

Когда я писал эту книгу, последней была версия ksqlDB 0.14.0. Совместимость со старыми и новыми версиями ksqlDB менее вероятна, чем в случае Kafka Streams, из-за постоянного и быстрого развития этой технологии и отсутствия основной версии (например, 1.0) на момент публикации книги. Будут приложены все силы, чтобы своевременно обновлять исходный код при включении критических изменений и размещать эти обновления в отдельной ветке (например, ksqldb-0.15). Однако для опробования примеров из этой книги рекомендуется не использовать версии старше 0.14.0.

Условные обозначения

В этой книге используются следующие условные обозначения.

Курсив

Курсивом выделены новые термины или важные понятия.

Моноширинный шрифт

Используется для листингов программ, а также внутри абзацев для обозначения таких элементов, как переменные и функции, базы данных, типы данных, переменные среды, операторы и ключевые слова, имена файлов и их расширения.

Моноширинный полужирный шрифт

Служит для обозначения команды или другого текста, который пользователь должен ввести самостоятельно.

Моноширинный курсив

Обозначает текст, который должен быть заменен значениями, введенными пользователем, или значениями, определяемыми контекстом.

Шрифт без засечек

Используется для обозначения URL, адресов электронной почты, названий кнопок и других элементов интерфейса, каталогов.

Эта пиктограмма указывает на совет или предложение.

Эта пиктограмма указывает на общее примечание.

Эта пиктограмма указывает на предупреждение.

Использование исходного кода примеров

Вспомогательные материалы (примеры кода, упражнения и т.д.) доступны для загрузки по адресу https://github.com/mitch-seymour/mastering-kafka-streams-and-ksqldb.

Если у вас возникнут вопросы технического характера по использованию примеров кода, направляйте их по электронной почте на адрес [email protected].

В общем случае все примеры кода из книги вы можете использовать в своих программах и в документации. Вам не нужно обращаться в издательство за разрешением, если вы не собираетесь воспроизводить существенные части программного кода. Если вы разрабатываете программу и используете в ней несколько фрагментов кода из книги, вам не нужно обращаться за разрешением. Но для продажи или распространения примеров из книги вам потребуется разрешение от издательства O’Reilly. Вы можете отвечать на вопросы, цитируя данную книгу или примеры из нее, но для включения существенных объемов программного кода из книги в документацию вашего продукта потребуется разрешение.

Мы рекомендуем, но не требуем добавлять ссылку на первоисточник при цитировании. Под ссылкой на первоисточник мы подразумеваем указание авторов, издательства и ISBN.

За получением разрешения на использование значительных объемов программного кода из книги обращайтесь по адресу [email protected].

Благодарности

Прежде всего хочу поблагодарить свою жену Элиз и дочь Изабель. Написание такого объемного труда потребовало огромных временных затрат, и ваше терпение и поддержка на протяжении всего процесса мне очень помогли. Как бы мне ни нравилось писать эту книгу, я очень скучал по вам обеим и с нетерпением жду новых встреч с дочкой.

Хочу также поблагодарить своих родителей, Энджи и Гая, за то, что научили меня ценить труд, каким бы тяжелым он ни был, и за то, что являлись бесконечным источником вдохновения. Ваша поддержка помогала мне преодолевать трудности на протяжении многих лет, и я бесконечно благодарен вам обоим.

Успешный результат моих усилий не был бы достигнут без людей, нашедших время на анализ содержания текста и давших бесценные отзывы и советы: это Матиас Дж. Сакс (Matthias J. Sax), Роберт Йокота (Robert Yokota), Нитин Шарма (Nitin Sharma), Рохан Десаи (Rohan Desai), Джефф Блейел (Jeff Bleiel) и Дэнни Эльфанбаум (Danny Elfanbaum). Спасибо вам всем за помощь в создании этой книги, она такая же ваша, как и моя.

Многие учебные примеры основаны на сценариях реального использования, и я в долгу перед всеми членами сообщества, которые без утайки поделились своим опытом работы с Kafka Streams и ksqlDB, будь то конференции, подкасты, блоги или личное общение. Ваш опыт помог написать эту книгу, в которой особое внимание уделяется практическому применению потоковой обработки. В частности, Нитин Шарма предложил к использованию для примеров по ksqlDB идеи, основанные на опыте Netflix, а Рамеш Срингери (Ramesh Sringeri) поделился своим опытом обработки потоков в детской больнице в городе Атланта (США) и вдохновил на создание учебного примера по прогнозной медицине. Спасибо вам обоим.

Особая благодарность Майклу Дрогалису (Michael Drogalis) за то, что поддержал идею написания этой книги. Спасибо за то, что познакомил меня со многими рецензентами моего текста, а также с Джеем Крепсом, любезно согласившимся написать предисловие. Спасибо Еве Бызек (Yeva Byzek) и Биллу Беджеку (Bill Bejeck), установившим высокую планку в реализации подобных книг. Спасибо вам обоим за ваш вклад в этой области.

В своей карьере я встретил много людей, которые помогли мне достичь нынешних высот. Спасибо Марку Конде (Mark Conde), Тому Стэнли (Tom Stanley) и Барри Боудену (Barry Bowden) — они были отличными наставниками и помогли мне стать хорошим инженером. Эрин Фусаро (Erin Fusaro) просто был надежной опорой и точно знал, что сказать, когда я чувствовал себя разбитым. Джастин Исаси (Justin Isasi) постоянно поддерживал меня и отмечал все мои усилия. Шон Сойер (Sean Sawyer) предложил мне несколько лет тому назад попробовать новую штуку под названием Kafka Streams, когда у меня не получалось решить задачу с использованием традиционных технологий. Томас Холмс (Thomas Holmes) и Мэтт Фармер (Matt Farmer) неоднократно делились со мной своим опытом и помогли мне стать хорошим инженером. Спасибо также команде Data Services в Mailchimp за помощь в решении некоторых действительно сложных проблем и за то, что вдохновляли меня своей работой.

Наконец, спасибо моим друзьям и семье, которые не бросают меня, даже когда я пропадаю на месяцы, погружаясь в работу над новым проектом. Спасибо, что остаетесь со мной.

От издательства

Ваши замечания, предложения, вопросы отправляйте по адресу [email protected] (издательство «Питер», компьютерная редакция).

Мы будем рады узнать ваше мнение!

На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.

Часть I. Kafka

Глава 1. Краткое введение в Kafka

Объем данных в мире растет в геометрической прогрессии. По информации Всемирного экономического форума, количество хранимых байтов уже намного превысило количество звезд в наблюдаемой Вселенной.

Говоря о данных, мы, как правило, подразумеваем множество байтов в хранилищах, в реляционных базах или в распределенных файловых системах. Во всех этих случаях данные находятся в состоянии покоя. Другими словами, они где-то неподвижно отдыхают, а если возникает необходимость их обработки, запускается какой-то запрос или задача.

Это традиционный способ восприятия данных. И они действительно могут накапливаться в различных местах, но значительно чаще они находятся в движении. Датчики Интернета вещей, медицинские приборы, финансовые системы, программное обеспечение для анализа пользователей и клиентов, журналы приложений и серверов и многое другое генерируют непрерывные потоки данных. Даже где-то осевшие данные перед этим некоторое время путешествовали по сети.

В ситуации, когда речь заходит об обработке в режиме реального времени, мы не можем просто дождаться, пока данные где-то накопятся, и только после этого приступить к созданию запросов и задач. Разумеется, и такие сценарии тоже практикуются, но часто обрабатывать, обогащать и преобразовывать данные требуется в момент их появления. Это значит, что нужен совсем другой подход. Необходима технология, предоставляющая доступ к данным в состоянии потока, технология, которая позволяет быстро и эффективно работать с непрерывными и бесконечными потоками. И здесь на помощь приходит Apache Kafka.

Apache Kafka (или просто Kafka) — платформа для приема, хранения и обработки потоков данных. Это крайне интересная технология, и я хотел рассказать вам про самую захватывающую, на мой взгляд, ее особенность: механизм потоковой обработки. Но, не зная, как функционирует платформа Kafka, невозможно понять, как работают библиотека Kafka Streams и база данных ksqlDB.

Именно поэтому первую главу я посвятил важным концепциям и терминологии, необходимым для понимания дальнейшего материала. Читатели, уже имеющие представление о платформе Kafka, смело могут ее пропустить.

Ниже найдете список тем, которые будут рассмотрены в этой главе.

• Как Kafka упрощает обмен данными между системами.

• Основные компоненты архитектуры Kafka.

• Какая абстракция хранилища наиболее точно моделирует потоки.

• Что обеспечивает надежность хранения данных в Kafka.

• Способы обеспечения высокой доступности и отказоустойчивости на уровне обработки данных.

Завершит главу инструкция по установке и запуску брокера Kafka. Для начала посмотрим на модель взаимодействия между его компонентами.

Модель взаимодействия

Наверное, один из самых распространенных видов взаимодействия между системами — синхронное взаимодействие в рамках модели «клиент — сервер». Под системами в данном контексте подразумеваются приложения, микросервисы, базы данных и все прочие компоненты, отвечающие за чтение и запись данных в сети. Изначально модель «клиент — сервер» предполагает прямую связь между системами, как показано на рис. 1.1.

 

Рис. 1.1. Простое в обслуживании соединение из точки в точку удобно при небольшом количестве систем

В качестве примера вы можете представить приложение, в синхронном режиме запрашивающее данные из базы, или набор микросервисов, напрямую обменивающихся данными.

Но такая модель с трудом масштабируется. Увеличение количества систем порождает запутанную сеть каналов связи, которую сложно проектировать и обслуживать. Рисунок 1.2 демонстрирует, насколько усложняется ситуация даже при относительно небольшом числе систем.

 

Рис. 1.2. Добавление систем порождает запутанную сеть каналов связи, которую трудно обслуживать

Вот некоторые недостатки модели «клиент — сервер».

• Сильная связь между системами, потому что для взаимодействия они должны знать о существовании друг друга. Такая связь затрудняет их поддержку и обновление.

• Синхронное взаимодействие не оставляет права на ошибку, ведь при выходе из строя одной из систем доставка данных не гарантирована.

• Каждая система может использовать собственные протоколы связи, варианты масштабирования при повышении нагрузки, стратегии обработки сбоев и другие параметры. В результате иногда приходится обслуживать сразу несколько видов систем, что осложняет поддержку, ведь в подобном случае набор приложений уже невозможно рассматривать как «стадо», с ним приходится работать как с «домашними питомцами».

• Принимающую систему легко загрузить сверх меры, так как скорость поступления новых запросов или данных не контролируется. Без буферизации система попадает в зависимость от поведения приложений, которые отправляют к ней запросы.

• Нет четкого представления о том, что передается между системами. В модели «клиент — сервер» основное внимание уделяется запросам и ответам на них, а не передаваемым данным, как это должно быть в системах, управляемых данными.

• Взаимодействие невоспроизводимо, что затрудняет восстановление состояния системы.

Брокер сообщений Kafka упрощает взаимодействие систем, выступая в качестве централизованного коммуникационного узла (его часто сравнивают с центральной нервной системой). Благодаря этому узлу системы могут отправлять и получать данные, ничего не зная друг о друге. Реализуемый им шаблон передачи сообщений «производитель — потребитель» (или pub/sub) дает более простую модель взаимодействия, показанную на рис. 1.3.

 

Рис. 1.3. Брокер сообщений Kafka устраняет сложности, возникающие при взаимодействии из точки в точку, выступая в роли хаба

Более подробный рис. 1.4 демонстрирует основные компоненты модели взаимо­действия Kafka.

 

Рис. 1.4. Основные компоненты платформы Kafka

 Вместо того чтобы использовать взаимодействие систем напрямую, производители (producers) просто публикуют данные в одной или нескольких темах, не заботясь о том, кто будет их читать.

 Темами, или топиками (topics), называются именованные потоки (или каналы) связанных данных, хранящиеся в кластере Kafka. Они играют ту же роль, что и таблицы в базе данных (группируют связанные данные). Однако они не навязывают конкретную схему, а хранят необработанные байты, что делает обращение с ними очень гибким1.

 Потребители (consumers) представляют собой процессы, которые читают данные из одной или нескольких тем. Они не общаются напрямую с производителями, а скорее «слушают» данные из любого интересного им потока.

 Потребители могут работать в группе, распределяя задачи по нескольким процессам.

В модели взаимодействий Kafka основное внимание уделяется потокам данных и легкости чтения и записи этих данных различными процессами. Такая модель имеет следующие преимущества.

• Системы становятся несвязанными и более простыми в обслуживании, поскольку для генерации и использования данных им не требуется информация о других системах.

• Асинхронная связь дает более надежные гарантии доставки. Вышедший из строя потребитель после возвращения в рабочее состояние просто продолжает работу с прерванного места. В группе задачи вышедшего из строя потребителя передаются другому члену группы.

• Появляется возможность стандартизировать протокол связи (кластеры Kafka используют высокопроизводительный двоичный протокол на базе TCP), а также стратегии масштабирования и механизмы отказоустойчивости, которые определяются группами потребителей. Это позволяет создавать более или менее единообразное программное обеспечение, и в целом оно проще для понимания.

• Потребители обрабатывают данные с доступной им скоростью. Необработанные данные хранятся надежным и отказоустойчивым способом, пока не появится возможность ими заняться. Другими словами, если поток, из которого читает потребитель, превращается в своего рода пожарный шланг, кластер Kafka срабатывает как буфер, предотвращая перегрузку приемника.

• События (events) позволяют классифицировать передаваемые данные. События представляют собой фрагменты данных с определенной структурой. Подробно мы поговорим о них чуть ниже в одноименном разделе, а пока важно запомнить: именно они дают возможность сосредоточиться на данных внутри потоков, избавляя нас от необходимости расшифровывать, что именно происходит на коммуникационном уровне, как это приходится делать в модели клиент-сервер.

• Восстановить состояние системы можно в любой момент, просто повторив события внутри темы.

Важное отличие используемой в Kafka модели «производитель — потребитель» от модели «клиент — сервер» заключается в том, что связь в Kafka не двунаправленная, то есть потоки текут только в одну сторону. Если система генерирует данные в одной теме, а обрабатывает (например, обогащает или преобразует) их другая система, обработанные данные требуется записать в новую тему, откуда ими сможет воспользоваться исходный процесс. Такой подход упрощает координацию, но меняет способы взаимодействия.

Исходя из того, что потоки текут в одном направлении и могут иметь несколько производителей, а ниже по течению несколько потребителей, легко спроектировать систему, слушающую все интересные для нее потоки байтов и генерирующую данные, которыми она хочет поделиться с одной или несколькими системами. С темами вы будете много работать в следующих главах (все создаваемые приложения Kafka Streams и ksqlDB будут читать и, как правило, делать запись в одну или несколько тем), так что, когда вы завершите чтение книги, все эти вещи станут для вас привычными и обыденными.

Теперь, когда вы знаете, что коммуникационная модель Kafka упрощает взаи­модействие систем, а именованные потоки (темы) действуют как каналы передачи данных, подробно рассмотрим, что происходит с потоками на уровне хранения.

Как хранятся потоки

Когда группа инженеров LinkedIn2 открыла потенциал управляемой потоками данных платформы, встал вопрос: как смоделировать неограниченные и непрерывные потоки данных на уровне хранения?

В конечном счете была выбрана концепция хранилища (https://oreil.ly/Y2Fe5), которая уже использовалась такими системами, как традиционные базы данных, хранилища пар «ключ — значение» и системы контроля версий. Это простой, но мощный журнал фиксации (commit log), который дальше я буду называть просто журналом.

Журналами в этой книге называются не регистрационные журналы приложений, содержащие информацию о запущенных процессах, а специальная структура данных, подробное описание которой я дам чуть ниже.

Итак, наш журнал представляет собой структуру данных, фиксирующую упорядоченную последовательность событий, причем обновление этой структуры возможно только путем присоединения новых записей (append-only). В качестве упражнения создадим простой журнал user_purchases и заполним его фиктивными данными. Это делается с помощью следующей команды:

# создаем файл журнала

touch users.log

 

# генерируем в журнале четыре фиктивных записи

echo "timestamp=1597373669,user_id=1,purchases=1" >> users.log

echo "timestamp=1597373669,user_id=2,purchases=1" >> users.log

echo "timestamp=1597373669,user_id=3,purchases=1" >> users.log

echo "timestamp=1597373669,user_id=4,purchases=1" >> users.log

В журнале мы обнаружим данные четырех пользователей, совершивших по одной покупке:

# выводим на экран содержимое журнала

cat users.log

 

# вывод

timestamp=1597373669,user_id=1,purchases=1

timestamp=1597373669,user_id=2,purchases=1

timestamp=1597373669,user_id=3,purchases=1

timestamp=1597373669,user_id=4,purchases=1

Еще раз повторю, что обновление журнала осуществляется только добавлением записей. Если пользователь с user_id=1 совершит вторую покупку, в конец журнала будет добавлена новая запись, потому что исходная информация об этом пользователе не подлежит изменениям:

# добавляем в журнал новую запись

echo "timestamp=1597374265,user_id=1,purchases=2" >> users.log

 

# выводим на экран содержимое журнала

cat users.log

 

# вывод

timestamp=1597373669,user_id=1,purchases=1 

timestamp=1597373669,user_id=2,purchases=1

timestamp=1597373669,user_id=3,purchases=1

timestamp=1597373669,user_id=4,purchases=1

timestamp=1597374265,user_id=1,purchases=2 

 Внесенная в журнал запись не подлежит изменениям. Это означает, что мы не можем редактировать ее, например, чтобы изменить значение счетчика покупок purchases.

 Для обновления значения счетчика добавим в конец журнала еще одну запись. В результате журнал будет содержать как старую, так и новую записи.

Системе, которая хочет узнать значение счетчика покупок для первого пользователя, достаточно по очереди прочитать записи в журнале. Интересующую ее информацию будет содержать последняя запись для user_id=1. Это подводит нас к следующему свойству журналов — упорядоченности.

В приведенном выше выводе журнала можно заметить, что записи упорядочены по временным меткам (первый столбец timestamp), но это простое совпадение. Никакой сортировки по временным меткам в Kafka не происходит. На самом деле каждая запись в журнале имеет фиксированную позицию. Если вывести содержимое журнала с номерами строк, вы увидите номер этой позиции в первом столбце:

# выводим содержимое журнала с номерами строк

cat -n users.log

 

# вывод

1 timestamp=1597373669,user_id=1,purchases=1

2 timestamp=1597373669,user_id=2,purchases=1

3 timestamp=1597373669,user_id=3,purchases=1

4 timestamp=1597373669,user_id=4,purchases=1

5 timestamp=1597374265,user_id=1,purchases=2

Без фиксированного порядка записей каждый процесс мог бы по-своему считывать обновления строки с user_id=1, в результате получая разные показатели для количества покупок этого пользователя. Именно упорядоченность журналов обеспечивает детерминированность3 обработки разными процессами4.

В приведенном выше примере для обозначения позиции каждой записи жур­нала использовались номера строк, в то время как Kafka рассматривает позицию каждой записи в распределенном журнале как смещение (offset). Смещение отсчитывается с 0, как показано на рис. 1.5. Такой подход позволяет группам потребителей при чтении из одного журнала фиксировать свою позицию.

 

Рис. 1.5. Смещение, которое потребитель прочитал/обработал, позволяет каждой группе потребителей запомнить свою позицию

Надеюсь, рассмотренный выше пример дал вам некоторое представление о способе хранения потоков в Kafka. Теперь можно перейти к изучению конструкций более высокого уровня, таких как темы и разделы.

Темы и разделы

При обсуждении коммуникативной модели Kafka я уже упоминал про такую концепцию, как именованные потоки или темы. Темы дают гибкий подход к хранящимся в них данным и могут быть как однородными (homogeneous topics), то есть содержащими данные только одного типа, так и неоднородными (heterogeneous topics), содержащими сразу несколько типов данных5 (рис. 1.6).

Как вы помните, для моделирования потоков на уровне хранения Kafka используются журналы фиксации, обновляющиеся только путем присоединения новых записей. Означает ли это, что каждой теме соответствует свой журнал? Не совсем. Дело в том, что Kafka — это распределенный журнал, а единственный объект сложно сделать распределенным. Для достижения некоторого уровня параллелизма в способах распространения и обработки журналов требуется много объектов. Вот почему темы Kafka разбиты на более мелкие блоки, называемые разделами (partitions).

 

Рис. 1.6. Существуют разные стратегии хранения событий в темах; однородные темы обычно содержат один тип событий (например, clicks), в то время как неоднородные темы содержат несколько типов событий (например, clicks и page_views)

Разделы представляют собой отдельные журналы (то есть структуры данных, которые мы обсуждали в предыдущем разделе этой главы). Именно на уровне разделов темы Kafka реализована абстракция журнала фиксации, и именно тут обеспечивается упорядочение записей, причем каждый такой раздел имеет свой собственный набор смещений. Глобального упорядочения на уровне тем нет, поэтому связанные записи часто направляются производителями в один раздел6.

В идеале данные равномерно распределяются по всем разделам темы. Но можно получить и разделы разного размера, как показано на рис. 1.7.

Тему можно разбить на произвольное количество разделов, и чем их больше, тем выше будут параллелизм и пропускная способность. Но слишком большое их число имеет и свои минусы7. Я еще не раз коснусь этого момента, а пока важно запомнить, что читать из раздела может только один потребитель из группы (при этом чтение из одного раздела доступно потребителям из разных групп, как показано на рис. 1.5).

 

Рис. 1.7. Тема Kafka с тремя разделами

Следовательно, для распределения нагрузки между N потребителями в одной группе потребуется N разделов. Идеальной является ситуация, когда потребителей в группе меньше, чем разделов в теме, из которой выполняется чтение. В этом случае каждый потребитель может обрабатывать несколько разделов. Если же потребителей в группе больше, чем разделов в теме, некоторые из потребителей будут простаивать.

Теперь, вооружившись новыми сведениями, мы можем улучшить определение темы. Тема — это состоящий из нескольких разделов именованный поток, в котором каждый раздел представляет собой журнал фиксации, хранящий упорядоченную последовательность записей и обновляемый только путем добавления новых записей.

Но что именно хранится в разделах темы? Об этом я расскажу вам ниже.

События

Обсуждение обработки данных в темах получится неполным без информации о том, какие именно данные там хранятся.

В литературе по Kafka, в том числе в официальной документации, их называют различными терминами: сообщения, записи, события. Я предпочитаю последний вариант и далее буду использовать именно его. Событие (event) представляет собой снабженную временной меткой пару «ключ — значение», описывающую, что именно произошло. Элементы, из которых оно состоит, показаны на рис. 1.8.

 

Рис. 1.8. Структура событий, которые хранятся в разделах тем

 В заголовки уровня приложения добавляют метаданные о событии, необязательный параметр. В этой книге он будет упоминаться достаточно редко.

 Ключи также необязательны, но они играют важную роль в распределении данных по разделам. В следующих главах вы увидите, что они используются для идентификации связанных записей.

 Каждому событию сопоставляется временная метка. Подробно мы поговорим о метках в главе 5.

 Значения содержат фактическую информацию о событиях в виде массива байтов. За превращение этих байтов в более информативную структуру (например, в объект JSON или в запись Avro) отвечает клиент. Процесс десериализации будет подробно рассмотрен в главе 3.

Теперь, когда вы лучше представляете структуру хранящихся в темах данных, рассмотрим кластерную модель развертывания Kafka.

Кластер и брокеры

При наличии централизованной точки связи особую важность приобретают вопросы надежности и отказоустойчивости. Кроме того, чтобы магистраль связи была способна выдерживать повышение нагрузки, она должна быть масштабируемой. Вот почему Kafka представляет собой кластер, а в хранении и извлечении данных принимают участие несколько машин, называемых брокерами (brokers).

Кластеры Kafka могут быть очень большими, даже охватывающими несколько центров обработки данных и географических регионов. Но я в основном буду рассказывать про кластер Kafka, состоящий из одного узла, поскольку для начала работы с Kafka Streams и ksqlDB этого вполне достаточно. В реальных условиях обычно требуется как минимум три брокера. Кроме того, как правило, желательно настроить репликацию тем, чтобы копии данных оказывались у нескольких брокеров. Вы увидите это в упражнении к текущей главе. Такой подход позволяет достичь высокой доступности и избежать потери данных в случае выхода из строя какой-либо из машин.

То есть, когда речь заходит о данных, хранящихся и реплицирующихся между брокерами, на самом деле имеются в виду индивидуальные разделы внутри темы. Например, на рис. 1.9 показана тема с тремя разделами, распределенными между тремя брокерами.

 

Рис. 1.9. Разделы распределены по доступным брокерам. Это означает, что тема может распределиться на несколько компьютеров в кластере Kafka

Такой подход позволяет иметь достаточно большие темы, обработка которых выходит за пределы возможностей отдельной машины. Для повышения отказоустойчивости и доступности при настройке темы задается коэффициент репликации. Например, при коэффициенте репликации 2 разделы будут храниться на двух брокерах, как показано на рис. 1.10.

При каждой репликации раздела между несколькими брокерами один из них назначается ведущим (leader). Именно он будет обрабатывать все запросы производителей и потребителей на чтение/запись из данного раздела. Другие брокеры, содержащие реплицированные разделы, называются ведомыми (followers) и просто копируют данные из ведущего брокера. Если ведущий перестает работать, его роль передается одному из ведомых.

 

Рис. 1.10. Увеличение коэффициента репликации до 2 приведет к тому, что разделы будут храниться на двух брокерах

Наконец, брокеры играют важную роль в поддержании членства в группах потребителей. Посмотрим, как это происходит.

Кроме того, поскольку нагрузка на кластер со временем увеличивается, его можно расширить, добавив брокеры и переназначив разделы. Это позволит перенести данные со старых машин на новую.

Группы потребителей

Брокер сообщений Kafka всегда стараются настроить на обеспечение высокой пропускной способности и малой задержки. Чтобы воспользоваться этим преимуществом на стороне потребителя, следует распараллелить работу нескольких процессов. Это делается с помощью групп потребителей.

Состав группы потребителей со временем может меняться. При увеличении нагрузки в нее могут добавляться новые потребители, а существующие периодически отключаются для планового обслуживания либо из-за неожиданного сбоя. Соответственно, нужен способ управления членством в группах и перераспределения выполняемой работы.

Для каждой группы потребителей выделяется специальный брокер, называ­емый координатором группы. Он отвечает за получение контрольных сигналов (heartbeats) от потребителей и запуск перебалансировки, если какой-то из потребителей перестает посылать такой сигнал. Схематично это представлено на рис. 1.11.

 

Рис. 1.11. Три потребителя, составляющие группу, посылают контрольные сигналы координатору

Каждому активному члену группы потребителей может быть выделен раздел. Рисунок 1.12 демонстрирует пример распределения работы между тремя активными потребителями.

 

Рис. 1.12. Три активных потребителя делят между собой нагрузку чтения/обработки в трех разделах темы Kafka

Когда какой-то потребитель выходит из строя и перестает посылать контрольные сигналы в кластер, его работа автоматически передается активным потребителям. Схематично это показано на рис. 1.13.

 

Рис. 1.13. Прекращение работы одного из потребителей ведет к перебалансировке нагрузки

Как видите, именно группы потребителей обеспечивают высокую доступность и отказоустойчивость на уровне обработки данных. Давайте на практике посмотрим, как все это работает. Для начала познакомимся с процессом установки Kafka.

Установка Kafka

Официальная документация (https://oreil.ly/rU-j_) содержит подробную инструкцию по установке брокера сообщений Kafka вручную. Но для простоты в большинстве представленных в книге примеров Kafka и приложения потоковой обработки будут развертываться в контейнере Docker (https://www.docker.com/).

Для установки Kafka воспользуемся инструментом Docker Compose и далее будем работать с образами Docker от Confluent8.

Первым делом скачайте Docker со страницы https://oreil.ly/1kS0h и выполните его установку.

Сохраните следующую конфигурацию в файл docker-compose.yml:

---

version: '2'

 

services:

    zookeeper: 

        image: confluentinc/cp-zookeeper:6.0.0

        hostname: zookeeper

        container_name: zookeeper

        ports:

        - "2181:2181"

    environment:

        ZOOKEEPER_CLIENT_PORT: 2181

        ZOOKEEPER_TICK_TIME: 2000

 

kafka: 

    image : confluentinc/cp-enterprise-kafka :6.0.0

    hostname: kafka

    container_name: kafka

    depends_on:

        - zookeeper

    ports:

        - "29092:29092"

    environment:

        KAFKA_BROKER_ID: 1

        KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'

        KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: |

            PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT

        KAFKA_ADVERTISED_LISTENERS: |

            PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092

        KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1

        KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1

 Первый контейнер zookeeper содержит хранилище ZooKeeper. Я не упомянул ZooKeeper во введении, так как на момент написания книги стоял вопрос о его удалении из Kafka. Но пока эта централизованная служба хранения мета­данных, таких как конфигурационные настройки темы, все еще продолжает использоваться.

 В контейнере kafka находится результат установки Kafka. Именно внутри него предстоит работать нашему брокеру, состоящему из одноузлового кластера. Здесь же будут выполняться и сценарии взаимодействия с кластером.

Локальный кластер Kafka запускается командой:

docker-compose up

Все, кластер Kafka приведен в рабочее состояние, и мы можем перейти к рассмотрению первого примера.

Hello, Kafka

Давайте посмотрим, как происходит создание темы, запись в нее данных производителем и, наконец, чтение данных темы потребителем. Первым делом нужно войти в контейнер с брокером Kafka. Это делается следующей командой:

docker-compose exec kafka bash

Создадим тему users. В этом нам поможет встроенный в Kafka консольный сценарий kafka-topics. Вот как выглядит эта процедура:

kafka-topics \ 

    --bootstrap-server localhost:9092 \ 

    --create \ 

    --topic users \ 

    --partitions 4 \ 

    --replication-factor 1 

 

# вывод

Created topic users.

 kafka-topics — встроенный в Kafka консольный сценарий.

 bootstrapserver — это разделенный запятыми список пар «хост/порт», которые представляют собой адреса брокеров Kafka в кластере.

 Для взаимодействия с темами Kafka применяются различные флаги, в частности --list, --describe и --delete. В примере использован флаг --create, создающий новую тему.

 Созданная тема получила имя users.

 Тему разбили на четыре раздела.

 Мы работаем с одноузловым кластером, поэтому коэффициент репликации оставим равным 1. Но в рабочей среде для повышения отказоустойчивости этому параметру обычно присваивают более высокое значение (например, 3).

Все сценарии, перечисленные в этом разделе, включены в комплект поставки файлов исходного кода Kafka. В стандартной версии Kafka они имеют расширение .sh (например, kafka-topics.sh, kafka-console-producer.sh и т.п.). Но на платформе Confluent расширения не используются. Именно поэтому в приведенном выше фрагменте кода написано kafka-topics, а не kafka-topics.sh.

Теперь выведем описание темы и ее конфигурацию:

kafka-topics \

    --bootstrap-server localhost:9092 \

    --describe \ 

    --topic users

 

# вывод

Topic: users PartitionCount: 4 ReplicationFactor: 1 Configs:

    Topic: users Partition: 0 Leader: 1 Replicas: 1 Isr: 1

    Topic: users Partition: 1 Leader: 1 Replicas: 1 Isr: 1

    Topic: users Partition: 2 Leader: 1 Replicas: 1 Isr: 1

    Topic: users Partition: 3 Leader: 1 Replicas: 1 Isr: 1

 Флаг --describe позволяет посмотреть конфигурационные настройки темы.

Добавим в тему данные с помощью встроенного сценария kafka-console-producer:

kafka-console-producer \ 

    --bootstrap-server localhost:9092 \

    --property key.separator=, \ 

    --property parse.key=true \

    --topic users

 Когда мы начнем работать с Kafka Streams и ksqlDB, будут использоваться процессы-производители, встроенные в базовую библиотеку Java, и сценарий kafka-console-producer понадобится разве что для тестирования и разработки.

 Для темы users создается набор пар «ключ — значение». Это свойство указывает, что в качестве разделительного символа будет использоваться запятая (,).

На экране появится приглашение на ввод. Введите перечисленные ниже пары «ключ — значение», и они будут добавлены в тему users. Для выхода из командной строки нажмите сочетание клавиш Control+C:

>1,mitch

>2,elyse

>3,isabelle

>4,sammy

Прочитаем содержимое нашей темы с помощью сценария kafka-console-consumer:

kafka-console-consumer \ 

    --bootstrap-server localhost:9092 \

    --topic users \

    --from-beginning 

 

# вывод

mitch

elyse

isabelle

sammy

 Сценарий kafka-console-consumer тоже входит в дистрибутив Kafka. Как и в случае со сценарием kafka-console-producer, в дальнейшем мы в основном будем пользоваться встроенными в Kafka Streams и ksqlDB процессами-потребителями, а не сценарием kafka-console-consumer, который останется для тестирования.

 Флаг --from-begin указывает, что потребление сообщений из заданной темы должно начаться с первого смещения, то есть с начала темы.

По умолчанию сценарий kafka-console-consumer выводит только значение, в то время как события содержат и другую информацию, например ключ, временную метку и заголовки. Чтобы их увидеть, добавим в сценарий дополнительные свойства9:

kafka-console-consumer \

    --bootstrap-server localhost:9092 \

    --topic users \

    --property print.timestamp=true \

    --property print.key=true \

    --property print.value=true \

    --from-beginning

 

# вывод

CreateTime:1598226962606 1 mitch

CreateTime:1598226964342 2 elyse

CreateTime:1598226966732 3 isabelle

CreateTime:1598226968731 4 sammy

Вот и все! Теперь вы умеете выполнять элементарные действия с кластером Kafka. Для остановки запущенных контейнеров после завершения работы используйте команду:

docker-compose down

Заключение

Коммуникационная модель Kafka упрощает взаимодействие систем, а ее быстрый и надежный уровень хранения дает возможность легко работать с потоками данных. Благодаря кластерному развертыванию Kafka обеспечивает доступность и высокую отказоустойчивость на уровне хранения, ведь данные копируются на другие машины, называемые брокерами. На уровнях обработки и потребления потоков высокая доступность, отказоустойчивость и масштабируемость рабочей нагрузки обеспечиваются способностью кластера получать контрольные сигналы и на их основе обновлять состав групп потребителей. Все эти особенности сделали брокер сообщений Kafka одной из самых популярных платформ для обработки потоков.

Этой информации вам хватит, чтобы приступить к работе с Kafka Streams и ksqlDB. В следующей главе вы узнаете, как библиотека Kafka Streams вписывается в более широкую экосистему Kafka, и на примерах увидите, как ей пользоваться.

1Хранящиеся в темах необработанные массивы байтов и их десериализация в структуры более высокого уровня, такие как объекты JSON/записи Avro, будут рассматриваться в главе 3.

2Изначально разработку Kafka вели Джей Крепс (JayKreps), Неха Нархеде (NehaNarkhede) и Джун Рао (JunRao).

3Детерминированность означает, что одни и те же входные данные всегда будут порождать одинаковый вывод.

4Вот почему традиционные базы данных используют для репликации журналы. Ведь именно там содержатся сведения обо всех операциях записи в базу. Достаточно по порядкуобработать их в реплике, чтобы снова гарантированно получить тот же набор данных.

5На эту тему Мартин Клеппманн (MartinKleppmann) написал интересную статью, доступную по адресу https://oreil.ly/tDZMm. Он рассматривает плюсы и минусы каждой из стратегий, а также причины, по которым можно предпочесть одну стратегию другой. Кроме того, в статье Роберта Йокоты (Robert Yokota), расположенной по адресу https://oreil.ly/hpScS, подробно объясняется, как поддерживать несколько типов событий при управлении схемами данных через реестр.

6Существуют разные стратегии разделения. В KafkaStreams и ksqlDB реализована популярная стратегия, в которой разделение выполняется на основе ключа записи (его можно извлечь из записи или установить вручную). Подробно мы поговорим об этом в следующих главах.

7Это и более длительные периоды восстановления после некоторых сбоев, и повышенное потребление ресурсов (дескрипторов файлов, памяти), и увеличение сквозной задержки.

8Существует множество образов Docker, подходящих для запуска Kafka. Но я рекомендую использовать образ от Confluent, в котором можно запускать еще и базу данных ksqlDB и реестр схем ConfluentSchemaRegistry.

9Начиная с версии 2.7, для вывода заголовков сообщений можно использовать флаг --property print.headers=true.

Часть II. Библиотека Kafka Streams