Apache Kafka — это распределённая платформа для стриминга событий. В контексте Go-разработки её чаще всего используют как высокопроизводительную очередь сообщений или систему обмена данными между микросервисами.
Ключевые концепции
Topic — категория/канал, куда публикуются сообщения. Аналог очереди или таблицы в БД.
Partition — единица параллелизма внутри топика. Каждая партиция — упорядоченная, неизменяемая последовательность сообщений. Партиции распределяются по брокерам, что даёт горизонтальное масштабирование.
Offset — уникальный идентификатор сообщения внутри партиции. Потребитель управляет своим смещением, что позволяет перечитывать сообщения или возобновлять чтение с места сбоя.
Consumer Group — группа потребителей, которые совместно читают партиции топика. Каждая партиция читается только одним потребителем внутри группы. Это ключевой механизм масштабирования потребления.
Broker — сервер Kafka. Кластер состоит из нескольких брокеров.
Replication Factor — количество копий каждой партиции. Обеспечивает отказоустойчивость.
Гарантии и модель доставки
- At-least-once — сообщение будет доставлено минимум один раз. Возможны дубликаты при повторной отправке.
- Exactly-once — достигается через идемпотентность продюсера и транзакции. Требует настройки
enable.idempotence=trueиacks=all. - At-most-once — сообщение может быть потеряно, но не будет дублировано.
Порядок сообщений гарантируется только внутри одной партиции. Если нужен глобальный порядок — используйте одну партицию, но это убивает параллелизм.
Практические детали для Go
Библиотеки:
segmentio/kafka-go— популярная, чистая Go-реализация, хорошая поддержка контекстов.confluent-kafka-go— обёртка над librdkafka на C, максимальная производительность, но сложнее в сборке и деплое.
Продюсер:
writer := kafka.NewWriter(kafka.WriterConfig{
Brokers: []string{"localhost:9092"},
Topic: "my-topic",
Balancer: &kafka.Hash{},
})
Важно: всегда закрывайте writer через defer writer.Close(). Используйте балансировку Hash или RoundRobin для равномерного распределения по партициям.
Консюмер:
reader := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{"localhost:9092"},
Topic: "my-topic",
GroupID: "my-group",
MinBytes: 10e3, // 10KB
MaxBytes: 10e6, // 10MB
})
Критично: после обработки сообщения нужно закоммитить offset. kafka-go делает это автоматически при вызове reader.ReadMessage(), но для точного контроля используйте reader.CommitMessages().
Типичные проблемы и как их избежать
1. Медленный потребитель (consumer lag)
- Увеличивайте количество партиций и потребителей в группе.
- Оптимизируйте обработку: выносите тяжёлые операции в отдельные горутины.
- Настройте
MaxBytesиMinBytesпод свой размер сообщений.
2. Потеря сообщений при перебалансировке
- Используйте
session.timeout.msиheartbeat.interval.msразумно. - В Go: обрабатывайте сигналы
SIGTERM/SIGINTи корректно закрывайте reader.
3. Дубликаты при at-least-once
- Делайте идемпотентную обработку на стороне потребителя (например, через уникальный ключ в БД).
- Используйте транзакционный продюсер для exactly-once.
4. Неправильный выбор партиции
- Если порядок важен — используйте ключ сообщения, который гарантирует попадание в одну партицию.
- Если порядок не важен — не задавайте ключ, Kafka распределит равномерно.
Когда Kafka — плохой выбор
- Нужна низкая задержка (< 10ms) — RabbitMQ или NATS подходят лучше.
- Очень маленькие сообщения (< 100 байт) — накладные расходы на метаданные велики.
- Простая очередь без хранения истории — Kafka хранит все сообщения до истечения retention, что избыточно.
Kafka — мощный инструмент, но требует понимания её модели и правильной настройки под конкретную нагрузку. В Go-проектах чаще всего используют её для event sourcing, CDC (Change Data Capture) и асинхронной интеграции микросервисов.