Если ты уже начал осваивать тестирование, но Кафка для тебя — по-прежнему только чешский писатель, у нас плохие новости….
Но есть и хорошие! Сегодня мы поговорим про брокер сообщений Apache Kafka: из чего он состоит, как работает, зачем нужен и где применяется. А главное — разберем на конкретных примерах, как его можно протестировать.

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

На рынке IT используются порядка 15 брокеров, среди них — RabbitMQ, Redis Streams, Apache Pulsar, NATS. При этом Kafka — один из наиболее широко распространенных. Его главное преимущество — способность быстро передавать и обрабатывать огромные потоки данных, поэтому Кафку часто интегрируют в масштабные системы. При этом она считается очень надежной — Кафке почти не страшны потери данных.
Как устроена Kafka?
Обычно устройство Кафки сравнивают с почтовыми ящиками, но мы считаем, что есть более точная аналогия. Представь себе IT-компанию, у которой есть несколько канбан-досок: на одной ставятся задачи по разработке нового сайта, другая доска — для маркетинга и рекламы, третья — для работы с клиентами и их заказами. На каждой доске есть несколько столбцов, и постоянно появляются новые задачи. Разные команды — разработчики, аналитики, проджект-менеджеры, тестировщики — постепенно берут разные задачи на этих досках в работу. При этом закрытые задачи не удаляются, а хранятся в архиве.
Кафка работает по схожему принципу: отдельные команды — это сервисы или программы, канбан-доски — это топики, столбцы — это партиции, а задачи — это сообщения.

Все еще сложно? Давай по порядку.
Чтобы было нагляднее, посмотрим на работу Кафки внутри игры «Битва покемонов». Это тренажер, который наша школа QA Studio разработала специально для освоения ручного тестирования. Тренажер помогает понять, как устроены API и микросервисная архитектура, научиться тестировать авторизацию и оплату, попрактиковаться в применении Charles, Postman, Jaeger и других инструментов. А сейчас он поможет нам разобраться, как может использоваться Кафка.
Сообщения — Messages
Сообщение — это единица данных, которая передается через Кафку. Если продолжать аналогию с канбан-доской, сообщение — это отдельная задача. Например, «создать двойную верификацию» или «пофиксить ошибку в консоли».
Сообщения состоят из трех обязательных частей:
Значение (value) — что собственно передается. Это могут быть события или данные, любая значимая информация.
Временная метка (timestamp) — время создания или публикации сообщения.
Офсет (offset) — уникальный номер сообщения. Чуть подробнее об офсетах — ниже.
Также у сообщений могут быть ключи и хедеры — это служебные данные, которые используются для маршрутизации и других технических нужд.
Скажем сразу, что для работы с Кафкой используются специальные программы. Они могут быть совсем базовыми — например, как командная строка Кафки (Kafka Command Line Interface). А могут иметь более удобный графический интерфейс для работы с топиками и сообщениями — например, Kafka UI, Conduktor или Offset Explorer. В нашем тренажере подключено сразу два интерфейса для Кафки: Kafka UI и Offset Explorer. Мы будем приводить скриншоты из Kafka UI.

Важная особенность сообщений в Кафке: они не изменяются, не удаляются сразу и строго упорядочиваются. Благодаря этому данные застрахованы от потери, их легко отслеживать и в случае необходимости — повторно обрабатывать.
Офсеты — Offsets
Остановимся чуть подробнее на одной из частей сообщения — офсете. Офсеты уникальны и присваиваются каждому сообщению по порядку. Продолжая аналогию с канбаном, офсет — это ID каждой задачи на доске.

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

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

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

Продюсеры также участвуют в обеспечении надежности Кафки — они могут быть настроены так, чтобы убедиться, что сообщение точно дошло и сохранилось в нужном топике.
Консьюмеры — Consumers
Консьюмеры — это программы, которые читают сообщения. В нашем примере разработчик, который зашел на канбан-доску, увидел задачу от аналитика и забрал ее в работу, выступает в качестве консьюмера.
Особенности работы консьюмеров:
Порядок сообщений важен. Консьюмеры читают сообщения из каждой части топика (партиции) в той последовательности, в которой эти сообщения были записаны. И как мы рассказывали выше — запоминают, до какого места они дошли, чтобы не обрабатывать данные повторно.
Несколько консьюмеров могут в группе работать над одним топиком. В таком случае каждая партиция внутри топика обрабатывается одним консьюмером. Это помогает распределить нагрузку и делает систему более надежной: если один консьюмер сломался, другой из той же группы подхватит его работу.
Партиции — Partitions
Итак, как мы уже упоминали, каждый топик в Kafka делится на одну или несколько частей, которые называются партициями. На нашей воображаемой канбан-доске каждая колонка: «Входящие задачи», «В работе», «Завершено» — это партиция. Новые задачи (сообщения) всегда добавляются в конец одной из этих колонок.

Деление топиков на партиции — ключевой момент в работе Кафки. Оно необходимо, чтобы вся система работала быстро с огромными объемами данных.
Чем полезны партиции:
Благодаря партициям несколько сервисов могут работать параллельно, не мешая друг другу — потому что каждую партицию может читать отдельная программа. Это сильно увеличивает скорость работы. Сравнимо с тем, как команда разработки и команда тестирования могут одновременно работать на одной канбан-доске: просто разработчики берут задачи из колонки «Бэклог», а тестировщики — из «Готово к тестированию».
Систему с партициями легко масштабировать, то есть расширять — ведь партиции могут находиться на разных серверах Кафки. Это позволяет безболезненно добавлять новые серверы и партиции, чтобы обрабатывать еще больше данных.
Zookeeper
Для управления всей этой сложной системой в Кафке используется отдельный сервис под названием Apache Zookeeper. Он координирует работу серверов Кафки, мониторит их доступность и роли — ведь на уровне партиций серверы часто делятся на «ведущих» (leader), которые записывают сообщения, и «запасных» (follower), которые хранят копии сообщений.
Zookeeper также следит за топиками и партициями. Если ведущий сервер какой-то партиции выходит из строя, Zookeeper перераспределяет нагрузку, назначая новый лидирующий сервер, чтобы сообщения продолжили записываться в нужную партицию без сбоев.
Продолжая аналогию с канбан-доской, Zookeeper — это управляющий менеджер, который сам не создает и не выполняет задачи, но координирует работу всех остальных сотрудников. Он распределяет нагрузку, назначает главных и второстепенных исполнителей, а если кто-то ушел на больничный — менеджер тут же найдет ему замену.

До недавнего времени Zookeeper был неотъемлемой частью системы, и без него Кафка просто не могла работать. Но начиная с версии 2.8 появилась новая архитектура под названием KRaft — и теперь Кафка умеет сама управлять данными без помощи Zookeeper. Но во многих продуктах Zookeeper по-прежнему используется, особенно если система была развернута до появления KRaft.
Как сообщения попадают в Kafka и что с ними происходит?

Продюсер отправляет сообщение.
Продюсер А на нашей схеме создает сообщение и отправляет его в нужный топик. Кафка записывает это сообщение в конец одной из партиций.Сообщение сохраняется.
Сообщения хранятся на диске сервера Кафки в течение установленного времени (или пока не наберется определенный объем), даже после того, как их прочитали. Это удобно, потому что разные программы могут читать одни и те же данные. А еще можно восстановить данные, если что-то пойдет не так.Консьюмер читает сообщение.
Консьюмер, который подписан на топик А, читает сообщения из тех партиций, которые ему назначены. В случае остановки консьюмер помнит, до какого сообщения он уже дошел (на схеме — сообщение с офсетом 111), и продолжает читать дальше.
Kafka на живом проекте
Разберемся, как именно работает Кафка внутри тренажера «Битвы покемонов».
«Битва покемонов» — это игра, где пользователь выступает в качестве тренера покемонов. Тренер создает своих покемонов и вызывает на битву покемонов других игроков. С каждой победой уровень покемонов и самого тренера растет, цель игры — дойти до самого высокого уровня и получить кубок.

Кафка используется на этапе, когда тренер запрашивает историю своих битв.


Пользователь нажимает кнопку «Заказать историю битв» на странице своего тренера. Запрос уходит в микросервис под названием «Бюро».
Микросервис «Бюро» создает новую запись в базе данных. Эта запись содержит номер заявки, ID тренера, дату и время отправки запроса и его выполнения. Также указывается статус заявки — изначально он всегда будет waiting (в ожидании).
Одновременно с этим микросервис отправляет сообщение в Кафку, в топик под названием battle_history_request. Микросервис в этом процессе — продюсер. В значении сообщения содержится номер заявки (тот самый, под которым появилась запись в базе данных) и ID тренера, который запросил историю.
-
На другом конце этой цепочки есть другая программа — монолит «API». Она подписана на топик battle_history_request в Кафке. Монолит в нашем случае — консьюмер. Как только новое сообщение получено, монолит начинает его обрабатывать.
Что важно: основной смысл Кафки заключается именно в четвертом пункте. Благодаря Кафке, монолит не сразу обрабатывает все запросы подряд, а последовательно читает сообщения из нужного топика. Если 100 пользователей одновременно нажмут кнопку с историей битв, монолит не получит 100 HTTP-запросов мгновенно, а прочитает сообщения из Кафки в своем темпе, не перегружаясь. И продюсер — микросервис «Бюро» может выполнять в это время другие задачи, вне зависимости от скорости работы монолита. К тому же, если монолит повредится, сообщения в топике сохранятся и не потеряются, и их можно будет обработать после восстановления.
Забрав данные из сообщения Кафки, монолит «API» собирает историю для конкретного пользователя: берет ID тренера, ищет список битв и создает CSV-файл. Готовый файл монолит отправляет на почту, привязанную к аккаунту тренера.
-
Как только файл отправлен, монолит обновляет запись в базе данных, которую создал там микросервис «Бюро» — меняет статус с waiting на delivered (доставлено).
А если что-то в этой цепочке пошло не так, у заявки выставляется статус failed (ошибка). Причину ошибки можно найти в наших логах в Kibana.

Такая архитектура позволяет обрабатывать запросы не сразу, а по очереди, не мешая другим микросервисам и игре в целом.
Как тестировщик взаимодействует с Apache Kafka
Для тестировщика система с Кафкой — целый новый мир, который можно покрыть десятками проверок. Нужно не просто убедиться, что консьюмер в итоге обработал нужные данные, а следить за каждым шагом, который сообщение проходит через Кафку. Обратимся снова к нашему примеру с «Битвой покемонов».
Что следует проверить
Проведем интеграционный тест:
Проходит ли система самый простой позитивный сценарий? Для начала посмотрим, как работает вся цепочка от нажатия кнопки «Заказать историю битв» до получения письма пользователем. Это нужно, чтобы убедиться, что все компоненты корректно взаимодействуют друг с другом через Кафку.
Проверим на запись в базе данных:
Появилась ли запись? Проверяем, что микросервис «Бюро» создал запись в базе данных и присвоил ей статус waiting.
Правильные ли данные в записи? Нужно убедиться, что в записи правильно заполнены все поля: номер заявки, ID тренера, дата и время отправки запроса.
Посмотрим на сообщение от продюсера детальнее:
Ушло ли сообщение в принципе? Нужно убедиться, что микросервис «Бюро» отправил сообщение.
Куда ушло сообщение? Оно должно попасть в правильный топик и в правильную партицию.
Что передается внутри сообщения? В значении сообщения должны быть все предусмотренные данные (в нашем случае — номер заявки и ID тренера) в правильном формате JSON, без нарушений синтаксиса.
Проследим за чтением сообщения консьюмером:
Получил ли монолит сообщение? Монолит «API» должен прочитать сообщение из определенного топика.
Не появились ли дубликаты? Убедимся, что система правильно работает, если один пользователь запрашивает историю битв несколько раз подряд. Дублей внутри Кафки при этом возникать не должно, ведь сообщениям все равно должны присваиваться разные офсеты.
Правильный ли порядок обработки? Сообщения должны обрабатываться строго в том порядке, в котором они были отправлены в партицию. Это важно, если разные пользователи делают несколько запросов одновременно.
Посмотрим на обработку запроса консьюмером:
Что будет, если у консьюмера не получится обработать сообщение? Мы можем вручную создать сообщение с неверным синтаксисом JSON и положить его в топик напрямую (подробнее об этом — ниже). Дойдя до такого сообщения, монолит «API» не должен зависать и ломаться — его задача просто перейти к следующему сообщению.
Правильно ли формируется история битв? Обязательно проверим, что файл с историей битв содержит верные и полные данные: все столбцы, предусмотренные документацией заполнены, правильно указаны ID победителей и проигравших, все битвы принадлежат тому тренеру, который их запрашивал.
Верный ли формат файла? Файл должен быть в CSV.
Проверим отправку письма с историей битв:
Отправлено ли письмо? Убедимся, что письмо с файлом истории битв ушло нашему тренеру.
А что в письме? Проверим тему, текст письма и то, что файл с историей битв прикреплен и открывается без ошибок в разных почтовых клиентах и браузерах.
Отследим обновление статуса в базе данных:
Изменился ли статус после обработки сообщения? Проверяем, что монолит сменил статус записи в базе данных с waiting на delivered, если письмо было успешно отправлено.
Корректно ли обрабатывается ошибка? Если мы симулировали ошибку на стороне монолита, запись в базе данных должна иметь статус failed, а ошибка — отображаться в логах.
Дополнительно не стоит забывать про проверки на устойчивость, скорость работы и нагрузку — даже в самом простом виде. Так, можно имитировать медленный интернет или полное его отсутствие, отправить несколько сотен запросов одновременно, а еще посмотреть, как быстро обрабатывается каждое сообщение. Наконец, можно попросить разработчика отключить один или несколько серверов Кафки — чтобы проверить, как консьюмер перестраивается на другие сервера, не теряются ли при этом сообщения и не обрабатываются ли они дважды.
Также можно проверять работу брокера, отправляя сообщения напрямую в Кафку. Как мы отмечали выше, для визуализации работы Кафки существует несколько программ. Большинство из них позволяют создавать сообщения вручную и отправлять их сразу в Кафку. Это может быть полезно, когда кнопка «Заказать историю битв» недоступна — например, если тренер еще не провел ни одной битвы. В таком случае тестировщик может самостоятельно создать сообщение в формате JSON прямо в нужном топике battle_history_request и посмотреть на поведение системы.


Какие могут быть ошибки?
Этот список, конечно, неполный — внутри самого брокера сообщений или интегрированных с ним программ могут возникать разные сбои. Мы обозначим наиболее уязвимые связи.

В сообщении отображаются неправильные данные. Если продюсер (в нашем случае — микросервис) отправит сообщение с неполными или ошибочными данными, то история битв будет сформирована неправильно.
Сообщение может потеряться на пути от продюсера к консьюмеру. Сообщение может не дойти до Кафки, не сохраниться там или монолит может его не прочитать. Такое случается из-за ошибок в настройках, проблем с интернетом или сбоев в программах, интегрированных с Кафкой.
Появляются сообщения-дубликаты. Иногда одно и то же сообщение может быть обработано несколько раз. Например, в монолите произошел сбой до того, как он успел подтвердить обработку — и тогда он повторно вернется к тому же сообщению.
Сообщения обрабатываются в неправильном порядке. Если Кафка настроена неверно, сообщения в одном топике могут обрабатываться не строго по порядку, что может привести к ошибкам в логике.
Консьюмер не читает сообщения. Консьюмер (в нашем случае — монолит) может не получить сообщение, если он сам работает с ошибками, неправильно подключен к Кафке, или просто не успевает обрабатывать все сообщения.
Статус записи в базе данных не меняется. Если монолит работает некорректно, он может не выставлять статус delivered у записи в базе данных — даже если обработка уже завершена, что тоже может привести к ошибкам во всей цепочке.
Произошел глобальный сбой в одном из компонентов. Если какая-то часть системы сломается (микросервис «Бюро», сервер Кафки, монолит «API», база данных), то весь процесс может остановиться.
Письмо не доходит. Проблемы с почтовым сервисом могут привести к тому, что финальный файл не дойдет до пользователя.
Что в итоге
Apache Kafka — это мощный и гибкий инструмент, который потенциально позволяет работать с колоссальными объемами данных. Кафка и ее аналоги уже давно стали стандартом во многих современных системах — поэтому тестировщику важно понимать, как работают брокеры сообщений, в чем их главная ценность и потенциальные уязвимости.
И мы очень надеемся, что наша статья помогла тебе с этим разобраться :)
Над статьей работали:
Герман Дольников
Анастасия Андрусёва
Больше полезных материалов в нашем тг-канале: https://t.me/qa_studio