Где используется Kafka: сценарии и установки
| Четыре установки | |
| Сбор логов | |
| Событийные микросервисы | |
| Потоковая обработка | |
| Многоконтейнерная установка в Docker Compose | |
| Когда Kafka не нужна | |
| Похожие статьи |
Четыре установки
Одно и то же ядро Kafka — дописываемые топики, группы консюмеров, повторное чтение — обслуживает очень разные архитектуры. Ниже четыре установки в порядке роста сложности, каждая — минимальная рабочая конфигурация. Последняя — полный многоконтейнерный проект, который можно запустить.
Сбор логов
Изначальная нагрузка Kafka: каждый сервис складывает логи в центральное место вместо локальных файлов, которые никто не читает. Один топик на сервис (или на уровень важности), крошечные продюсеры с отправкой без ожидания ответа и несколько консюмеров, раздающих данные дальше — в хранилище и в поиск:
# каждый сервис дописывает в свой топик; ротацией занимается retention web-1 -> топик logs-web api-2 -> топик logs-api worker-3 -> топик logs-worker # консюмеры в своих группах — каждый видит все события: холодное хранилище <- все топики логов поисковый индекс <- все топики логов # пейджер: только severity >= error алерты <- топик logs-critical
Создать топик с недельным хранением для таких данных:
Создание топика
kafka-topics.sh --create --topic logs-web --bootstrap-server kafka:9092
Событийные микросервисы
Сервисы перестают вызывать друг друга и начинают объявлять факты.
Сервис заказов дописывает order.placed в один
топик и идёт дальше; резервирование склада, письма и аналитика читают
этот топик каждый в своём темпе. Добавить четвёртую реакцию — скажем,
антифрод — значит развернуть ещё одну группу консюмеров, причём вся
история доступна для перечитывания с первого дня. Без изменений
в сервисе заказов, без простоев.
Контракт, на котором всё держится, — схема событий: держите события
маленькими, версионированными и обратно совместимыми, потому что старые
события переживают любой деплой консюмеров.
Потоковая обработка в реальном времени
Когда вопрос — про сам поток: сколько оформлений в минуту, где группируются ошибки, — потоковый процессор читает топики и пишет производные топики обратно. Подсчёт слов поверх топика кликов — канонический пятиминутный пример; боевые задачи добавляют окна (агрегаты за минуту) и соединения с медленно меняющимися таблицами. Kafka Streams и ksqlDB закрывают этот паттерн без отдельного кластера обработки при умеренных нагрузках.
Многоконтейнерная установка в Docker Compose
Полная картина: три контейнера в одной Compose-сети — API магазина, принимающее HTTP-заказы, складской воркер и сама Kafka, причём каждое слово между сервисами идёт через топик. API никогда не вызывает воркер напрямую, а воркер никогда не опрашивает API.
Структура проекта
# shop/ — всё ниже лежит в одном каталоге compose.yaml producer/Dockerfile producer/requirements.txt producer/app.py consumer/Dockerfile consumer/requirements.txt consumer/worker.py
compose.yaml — один KRaft-брокер плюс два сервиса приложений
# compose.yaml — одиночный KRaft-брокер, ZooKeeper нигде нет services: kafka: image: apache/kafka:latest container_name: shop-kafka ports: - "9092:9092" environment: KAFKA_NODE_ID: 1 CLUSTER_ID: MkU3OEVBNTcwNTJENDM2Qk KAFKA_PROCESS_ROLES: broker,controller KAFKA_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093 KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true" # один брокер: факторы репликации — только 1, иначе создание топиков зависнет KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 producer: build: ./producer container_name: shop-producer ports: - "8000:8000" depends_on: - kafka consumer: build: ./consumer container_name: shop-consumer depends_on: - kafka
Строки про факторы репликации — те, что все забывают: значения по умолчанию рассчитаны на три брокера, а на одиночном создание топиков виснет навсегда. В advertised listeners — имя сервиса Compose kafka: именно по этому хосту должны стучаться оба Python-сервиса.
producer/Dockerfile и зависимости
# producer/Dockerfile (consumer/Dockerfile такой же, с worker.py) FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py . CMD ["python", "app.py"] # requirements.txt — одна строка: kafka-python
producer/app.py — HTTP на входе, Kafka на выходе
import json import time from http.server import BaseHTTPRequestHandler, HTTPServer from kafka import KafkaProducer producer = KafkaProducer( bootstrap_servers="kafka:9092", value_serializer=lambda item: json.dumps(item).encode("utf-8"), ) class OrderHandler(BaseHTTPRequestHandler): def do_POST(self): length = int(self.headers.get("Content-Length", 0)) payload = self.rfile.read(length).decode("utf-8") producer.send("orders", {"raw": payload, "at": time.time()}) producer.flush() body = b"accepted" self.send_response(200) self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) HTTPServer(("0.0.0.0", 8000), OrderHandler).serve_forever()
consumer/worker.py — Kafka на входе, резерв склада на выходе
import json from kafka import KafkaConsumer consumer = KafkaConsumer( "orders", bootstrap_servers="kafka:9092", group_id="stock-keepers", value_deserializer=lambda blob: json.loads(blob.decode("utf-8")), auto_offset_reset="earliest", ) for message in consumer: order = message.value print("reserve stock for", order, flush=True)
Group id — ручка масштабирования: запустите трёх воркеров через --scale consumer=3, и Kafka разложит партиции топика между ними. Упавший воркер после рестарта продолжит с зафиксированного офсета — ни один заказ не обработается дважды и ни один не потеряется.
Запуск: сначала брокер, потом приложения
docker compose up -d kafka
Дождитесь, пока docker compose logs kafka покажет стартовавший брокер, затем поднимайте приложения (в два шага, потому что depends_on упорядочивает старт, но не ждёт готовности, — консюмер, стартовавший при холодном брокере, упадёт вместо повторных попыток):
docker compose up -d --build
Отправить заказ, посмотреть на воркер
curl -X POST localhost:8000 -d "{"sku":"book","qty":2}"
docker compose logs -f consumer
Строка reserve stock for … в логе доказывает весь путь: HTTP → продюсер → топик orders → консюмер. Разобрать всё можно командой docker compose down; добавьте -v, чтобы удалить и данные брокера.
Когда Kafka не нужна
Три честных случая против. Первый — запрос/ответ: если вызывающий ждёт ответа, пара топиков всегда проиграет HTTP-вызову или RPC. Второй — крошечная нагрузка: несколько сообщений в минуту потянет лёгкая очередь (или таблица в базе) с куда меньшим хозяйством, чем даже один брокер. Третий — строгий глобальный порядок от многих писателей: Kafka упорядочивает только внутри партиции, так что проекты с единой мировой последовательностью будут бороться с инструментом, а не пользоваться им.
Article author: Arthur Isaev
| Kafka |