Содержание урока · 10 разделов
- go get: установка kafka-go в проект
- Локальный брокер kafka в Docker Compose
- Первый топик: kafka.Client и консоль
- Чек-лист рабочей среды
- Проверка связи: консольные продюсер и консьюмер
- Команды урока одним столом
- Почему держать брокер под рукой до конца курса
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Kafka — единственный инструмент курса, который нельзя почувствовать в песочнице: клиент говорит по TCP с живым брокером, поэтому без окружения всё сведётся к чтению. Хорошая новость в том, что среда собирается один раз и живёт годами: библиотека подключается к проекту одной командой, а брокер — это один Docker-контейнер, который стартует за полминуты и не просит конфигов, если знать три переменные. В этом уроке пройдём весь путь от пустой папки до первого сообщения в собственном топике: go mod init и go get, docker-compose.yml с современным single-node брокером без ZooKeeper, создание топика из Go и из консоли, проверка продюсером и консьюмером. Каждая команда урока копируется как есть — к концу у вас будет ровно та площадка, на которой построены все двадцать пять уроков курса. Сверх этого ничего не понадобится: ни кластера, ни ZooKeeper, ни ручных конфигов — несколько переменных окружения закрывают весь KRaft-режим для одиночной ноды.
go get: установка kafka-go в проект#
Установка kafka-go выглядит стандартно для Go-модулей: создаём модуль командой go mod init и добавляем зависимость github.com/segmentio/kafka-go через go get — импорт-путь совпадает с репозиторием, никаких спецдвижений. Библиотека написана на чистом Go: внутри нет ни строчки cgo, поэтому сборка не требует GCC и системных заголовков, кросс-компиляция из macOS в Linux работает из коробки, а итоговый бинарник — обычный статичный файл, как у любого сервиса на стандартной библиотеке. Это не мелочь: альтернативный клиент confluent-kafka-go тянет за собой C-библиотеку librdkafka, и его сборка в Alpine-образе превращается в отдельный проект. Версия фиксируется в go.mod как обычно; библиотека живёт по классическому тегированию v1 и спокойно обновляется между минорами. Если рядом в проекте уже лежат модули из курсов по базовому Go, ничего перенастраивать не нужно — kafka-go встанет в тот же go.mod и тот же кэш модулей. Проверка сборки проста: импортируйте пакет в любой файл и выполните go build — компилятор подтянет модуль и честно покажет ошибку, если версия не сошлась. Всё это делает kafka-go особенно удобной для CI: сборка тестового образа не тянет ни C-инструментарий, ни системных зависимостей.
Три команды для нового проекта
GOmkdir kafka-demo && cd kafka-demo
go mod init kafka-demo
go get github.com/segmentio/kafka-go
# проверить, что зависимость легла в go.mod
cat go.modgo mod init создаёт модуль с именем kafka-demo, go get подтягивает последнюю версию библиотеки и пишет её в go.mod вместе с контрольными суммами в go.sum. Отдельного клиента ставить не нужно: kafka-go — единственная зависимость курса, без librdkafka и системных пакетов. Дальше импорт в коде выглядит как github.com/segmentio/kafka-go и сокращается до kafka.
Локальный брокер kafka в Docker Compose#
Современный Kafka не требует ZooKeeper: со версии 2.8 доступен режим KRaft, где кворум метаданных живёт внутри самих брокеров, а с третьей версии он стал основным путём. Для разработки берём single-node конфиг: один контейнер одновременно и брокер, и контроллер. Минимальный docker-compose.yml для образа bitnami/kafka выглядит так, как в блоке ниже, и в нём важно понимать три переменные. PROCESS_ROLES и CONTROLLER_QUORUM_VOTERS включают KRaft-режим для одиночного узла. LISTENERS описывает, на каких портах брокер принимает соединения: 9092 — клиентский, 9093 — внутренний для контроллера. А ADVERTISED_LISTENERS — адрес, который брокер сообщает клиентам в ответе «я здесь»: если написать там имя контейнера, ваш Go-код с хоста получит недостижимый адрес — классическая ловушка, ниже отдельный коллаут. Тома наружу выносим и порт 9092; данные живут в именованном томе kafka_data, поэтому рестарты и пересоздание контейнера учебный топик не стирают. Файл целиком копируется в проект без правок: все переменные обязательны, и их сокращение — самый быстрый способ получить брокер, который поднимается, но не отвечает.
docker-compose.yml одного брокера
GOservices:
kafka:
image: bitnami/kafka:3.7
container_name: kafka
ports:
- "9092:9092"
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
volumes:
- kafka_data:/bitnami/kafka
volumes:
kafka_data:Одна услуга, один образ, шесть переменных: KRaft-режим без ZooKeeper, клиентский слушатель на 9092, контроллерный на 9093. Advertised-адрес localhost:9092 — то, что брокер скажет вашему Go-коду с хоста, поэтому он отличается от внутреннего адреса слушателя. Именованный том kafka_data держит данные лога между рестартами: без него пересоздание контейнера стирает все топики.
Типичная картина: compose поднялся, kafka-topics внутри контейнера работает, а Go-код висит или падает с io timeout. Причина — advertised.listeners с адресом вроде kafka:9092: клиент получает метаданные и честно идёт по указанному адресу, а имя kafka резолвится только внутри сети Docker, не с хоста. Симптом особый: соединение установлено, но дальше ничего — вы стучитесь в контейнер, которого снаружи нет. Лечится одним словом localhost в ADVERTISED_LISTENERS, как в конфиге выше. Правило на память: advertised-адрес — то, куда брокер отправляет клиента в ответе, а не то, куда клиент стучится изначально.
Первый топик: kafka.Client и консоль#
Топик в Kafka не создаётся автоматически первой записью — по умолчанию брокер отвечает ошибкой на запись в несуществующий канал, и это осознанная защита от опечаток. Создать топик можно двумя путями. Первый — консольная утилита kafka-topics.sh внутри контейнера: она нужна один раз, чтобы поднять топик orders с тремя партициями и фактором репликации 1. Второй — из кода, и это то, что делают сервисы при старте: kafka-go открывает Admin API через структуру kafka.Client с адресом кластера в поле Addr, а создание топиков — её метод CreateTopics. Он принимает контекст и CreateTopicsRequest со срезом TopicConfig; в каждом конфиге важны три поля: имя, NumPartitions и ReplicationFactor — для локального брокера реплика всегда одна, потому что брокер один. Полное управление топиками из Go — конфиги, изменение числа партиций, удаление — отдельный урок про Admin API, а здесь Client.CreateTopics выполняет роль init-миграции: сервис стартует, гарантирует нужный топик и работает дальше. В командной работе создание топиков обычно выносят в скрипт деплоя, чтобы код и инфраструктура не спорили, кто автор канала. В учебном проекте проще держать клиента и запрос в main.go перед стартом продюсера: против чистого брокера сервис сам себя готовит, а против уже настроенного просто повторяет знание о топике.
Создание топика из Go
GOclient := &kafka.Client{Addr: kafka.TCP("localhost:9092")}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
res, err := client.CreateTopics(ctx, &kafka.CreateTopicsRequest{
Topics: []kafka.TopicConfig{
{
Topic: "orders",
NumPartitions: 3,
ReplicationFactor: 1,
},
},
})
if err != nil {
log.Fatal(err)
}
for topic, terr := range res.Errors {
fmt.Println(topic, terr)
}kafka.Client — вход в Admin API: структура с адресом кластера и Transport'ом по умолчанию, потокобезопасная и долгоживущая. Метод CreateTopics принимает CreateTopicsRequest со срезом TopicConfig и возвращает ответ: err означает сбой запроса целиком, а судьба каждого топика приходит в карте Errors. Для существующего топика там лежит TopicAlreadyExists — её отфильтровывают через errors.Is, свежесозданный топик даёт nil. kafka.TCP превращает строку адреса в net.Addr.
Чек-лист рабочей среды#
- go mod init и go get выполнены, kafka-go видна в go.mod.
- docker compose up -d прошёл, контейнер kafka отвечает на docker ps со статусом Up.
- ADVERTISED_LISTENERS указывает на localhost:9092 — иначе Go-код не дозвонится с хоста.
- Топик orders создан с тремя партициями и виден в списке kafka-topics.sh --list.
- Данные лежат в именованном томе kafka_data, а не в файловой системе контейнера.
- Порт 9092 свободен на хосте: локальный конфликт портов — второй по частоте сюрприз после advertised listeners.
docker compose down останавливает и удаляет контейнер, но оставляет том kafka_data: после docker compose up -d топик и сообщения на месте. Команда с флагом -v стирает и том — полный сброс окружения до чистого брокера. Первое пригодится при ежедневной работе, второе — когда хочется убедиться, что сервис умеет стартовать с нуля и пересоздавать свои топики. Разницу стоит запомнить на уровне пальцев: первый — ежедневная работа, второй — кнопка аварийного сброса.
Проверка связи: консольные продюсер и консьюмер#
Прежде чем писать Go-код, стоит убедиться, что канал жив, — и для этого в образе есть консольные утилиты, которые сами же и пригодятся при отладке весь курс. kafka-console-producer.sh читает строки из стандартного ввода и отправляет каждую как сообщение: набрали текст, нажали Enter — уехало. kafka-console-consumer.sh с флагом --from-beginning вычитывает топик с самого старого живого оффсета и печатает сообщения в терминал, оставаясь висеть и показывать новые. Запустив оба в двух терминалах, вы увидите круговорот: строка ушла из продюсера — появилась у консьюмера. Эта пара — быстрее любой диагностики: если консольные утилиты внутри контейнера ходят, а Go-код нет, проблема в адресе с хоста, то есть в advertised listeners; если и консоль молчит, смотрите, жив ли контейнер и существует ли топик. Утилиты принимают те же --bootstrap-server, что и клиент: единственная точка правды — адрес брокера. Оба инструмента живут в том же образе, что и брокер, поэтому работают даже там, где нет ни Go, ни вашего кода: удобно проверять окружение до репродукции бага. Если консьюмер печатает ваши строки — канал исправен, и любую следующую проблему стоит искать в коде, а не в окружении.
Команды урока одним столом#
| Действие | Команда |
|---|---|
| Поднять брокер | docker compose up -d |
| Статус окружения | docker compose ps |
| Создать топик | docker exec kafka kafka-topics.sh --create --bootstrap-server localhost:9092 --topic orders --partitions 3 |
| Список топиков | docker exec kafka kafka-topics.sh --bootstrap-server localhost:9092 --list |
| Прочитать топик с начала | docker exec kafka kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic orders --from-beginning |
| Полный сброс с данными | docker compose down -v |
Почему держать брокер под рукой до конца курса#
Код этого курса — сетевой по определению, и честная оговорка будет звучать одинаково в каждом уроке: песочница сайта не умеет сеть и TCP-сокеты, поэтому kafka-код запускается локально против брокера в Docker, а интерактивы песочницы показывают тематические программы на чистом Go, обычно эмулирующие нужную механику — от хэша ключа до батчей. Такой формат не потеря: механики партиционирования и адресации удобнее щупать интерактивами, а ход сообщений в живом брокере — это десять минут подъёма окружения один раз. С этого урока у вас есть всё: клиент в go.mod, брокер на localhost:9092, топик с тремя партициями и утилиты, чтобы заглянуть внутрь в любую секунду. Дальше каждый урок будет давать код, который стоит против этой площадки: скопировали — запустили — увидели своими глазами. Именно на этом цикле Kafka перестаёт быть набором терминов и становится инструментом, который вы читаете и отлаживаете без страха. К этому же уроку стоит вернуться при смене машины: чек-лист выше — самодостаточная инструкция восстановления окружения за пять минут.
Проверка: адрес, который видит клиент
Go-код на хосте соединяется с брокером, но запись висит. Внутри контейнера утилиты работают. Первое, что проверяем?
Проверка: что делает go get
Что происходит после go get github.com/segmentio/kafka-go?
Что станет с топиком после рестарта?
Прочитайте код и выберите, что он выведет:
# в compose объявлен именованный том:
# volumes: kafka_data:/bitnami/kafka
docker compose down
docker compose up -d
docker exec kafka kafka-topics.sh \
--bootstrap-server localhost:9092 --listСоберите создание топика
Допишите вход в Admin API, сборку адреса и вызов создания топика.
Соберите путь до первого сообщения
Расставьте шаги от пустой папки до записи: модуль, зависимость, брокер, топик, Writer, отправка, закрытие.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: установка и окружение
Среда готова: клиент в go.mod, брокер на localhost:9092, топик orders с тремя партициями, консольные утилиты под рукой. Дальше по курсу: в уроке про Writer напишем первого продюсера и разберём WriteMessages с контекстом и ошибками; в уроке про Reader — первого консьюмера. Создание топиков из кода вернётся с полным Admin API в уроке шестнадцатом. Домашнее задание — собрать всё из урока руками: эта площадка будет колесом всей телеги, и час, вложенный сейчас, вернётся на каждом следующем уроке. Если что-то не взлетело — начните с коллаута про advertised listeners: девять из десяти проблем подключения живут именно там.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- kafka-go ставится через go get как обычная зависимость: чистый Go, без cgo и системных пакетов.
- Локальный брокер — один контейнер bitnami/kafka в режиме KRaft: без ZooKeeper, с томом для данных.
- Advertised listeners обязаны указывать на адрес, достижимый с хоста: localhost:9092 — для локальной разработки.
- Топики создаются явно: kafka.CreateTopics из кода или kafka-topics.sh из консоли.
- Консольные продюсер и консьюмер — быстрая диагностика: если консоль ходит, а Go нет, проблема в адресе с хоста.
- Kafka без ZooKeeper — это KRaft: single-node из шести переменных окружения поднимается за полминуты.
- down сохраняет том, down -v стирает всё: разница между рестартом окружения и полным сбросом.
- Песочница сеть не поднимает: kafka-код — локальный Go и брокер в Docker, интерактивы — программы на чистом Go.
Домашнее задание
Соберите среду по уроку и зафиксируйте её в репозитории проекта: docker-compose.yml из урока, скрипт setup.sh с go mod init, go get и созданием топика orders на три партиции. Проверьте полный цикл: compose up, CreateTopics через go run, отправка сообщения Writer'ом из четвёртого урока-тизера, чтение через kafka-console-consumer. Затем выполните docker compose down -v и прогоните скрипт заново — окружение должно восстанавливаться с нуля без ручных шагов. Сохраните compose и скрипт в репозитории: они пригодятся и при смене машины, и при переходе на новые версии брокера.
Личные заметкиПусто
Вопросы и ответы по уроку
Можно ли ставить Kafka без Docker?
Можно: скачать архив с сайта Apache, распаковать и запустить bin/kafka-server-start.sh с KRaft-конфигом из папки config/kraft. Но Java-окружение, ручные конфиги и очистка логов руками делают Docker-путь практичнее для разработки: контейнер поднимается одной командой и стирается одной командой.
Почему образ bitnami, а не Apache и не Confluent?
Все три рабочие. Bitnami даёт KRaft single-node в шести переменных окружения без ZooKeeper-контейнера, Confluent требует либо пару контейнеров, либо больше переменных, а чистый Apache — ручную правку конфигов. Для учёбы важна простота подъёма, а не производительностные флаги, поэтому курс держится bitnami; конфиги переносимы — все настройки из урока переезжают на любой образ с минимальными правками имён переменных.
Нужен ли ZooKeeper для учёбы?
Нет. Начиная с Kafka 3.x режим KRaft — штатный путь, и single-node из урока работает на нём. Отдельный контейнер ZooKeeper нужен только старым версиям 2.x и специфическим кластерным конфигурациям; для локальной разработки и этого курса он не требуется. Практический совет: какую бы сборку вы ни выбрали, убедитесь, что её версия поддерживает KRaft — 3.x и новее.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
Ebitengine: 2D-игры на Go
Курс Ebitengine для Go: игровой цикл Update/Draw, спрайты и анимации, ввод, коллизии, тайлмапы, камера, звук, сцены и сборка на платформы. 25 уроков.
- Понимать игровой цикл Update/Draw и Layout
- Рисовать спрайты, атласы и примитивы
- Обрабатывать ввод: клавиатура, мышь, тач
- Строить движение, коллизии и анимации