Зачем: тикет #23 — каждое цитируемое число курса сверено с живым стендом; до этого в уроках стояли заглушки. Что: - уроки 00–04: 18 маркеров заполнены числами свежего импорта (280 437 событий, нули в таблицах ошибок, счётчики ODS/DDS); - лаба 07: таблица manifest после дня 4 (374 092 / 34 801 / 5 388), переходящие визиты по стыкам (35/22/26), числа после дня 5; - лаба 08: каноническая граница трёх дней, пример замера свежести (лаг 3:45 модельного времени до догона, ETL ~29 с) и вернувшегося пользователя; две живые поправки разбора времени: убран принудительный UTC в разборе STG и суффикс +00:00 в сравнении границы (ловились только на живом стенде). Проверка: детерминизм подтверждён двумя независимыми циклами сброс→импорт→инкремент (числа manifest и checksum_sha256 дней 4 и 5 совпали бит в бит); chain-check зелёный; сценарий лабы 08 прогнан вживую, включая стоп/продолжение и красный full_refresh=false из урока 4; grep «сверить-на-стенде» пуст; ссылки и якоря целы; make test (219+31) и make lint зелёные; /ai-text-lint по лабам — без существенных находок. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
169 lines
13 KiB
Markdown
169 lines
13 KiB
Markdown
# Урок 0. Вводный по Kafka (Kafka UI, наблюдение)
|
||
|
||
> Формат: **наблюдение** — ничего не запускаем и не меняем, только смотрим.
|
||
> Пререквизит: обзорное видео по Kafka из роадмапа — оттуда ты уже знаешь слова
|
||
> «топик», «партиция», «offset», «consumer-группа», «lag». Этот урок связывает их
|
||
> с живым стендом, чтобы они перестали быть просто словами.
|
||
> Эталонный путь: сам Kafka UI на `http://localhost:8082`.
|
||
>
|
||
> Поток данных одной строкой:
|
||
> `готовый источник данных стенда → топики Kafka (партиции, offset'ы) → consumer-группа ClickHouse вычитывает`
|
||
>
|
||
> О чём урок простыми словами: ходим по Kafka UI и разглядываем поток — где лежат события,
|
||
> кто их читает и как Kafka помнит, до какого места уже дочитано.
|
||
|
||
---
|
||
|
||
## 1. Зачем и где в проде
|
||
|
||
В обычной кликстрим-аналитике между тем, кто **порождает** события (трекер на сайте, в
|
||
приложении), и тем, кто их **считает** (хранилище, BI), почти всегда стоит Kafka. Она —
|
||
буфер и общий журнал: producer пишет события, не зная и не дожидаясь, кто их прочитает;
|
||
consumer читает в своём темпе, не трогая producer'а. Если читатель отстал или прилёг —
|
||
события не теряются, они лежат в топике и ждут.
|
||
|
||
На нашем стенде роли уже расставлены: готовый источник данных стенда играет роль трекера
|
||
и пишет события в топики, а ClickHouse — это consumer, который их вычитывает. Для курса нам
|
||
важно не устройство источника, а сам путь данных: Kafka → STG → ODS → DDS → DM → Superset.
|
||
Файлы `data/*.jsonl` здесь не источник аналитики; пока это только кладовка готовых значений
|
||
для источника данных стенда.
|
||
|
||
В этом уроке мы не запускаем пайплайн и ничего не меняем — мы открываем Kafka UI и
|
||
**узнаём в живом кластере** те самые понятия из видео. Это разминка: в уроке 1 ты уже руками
|
||
увидишь, как эти же сообщения доезжают до таблиц ClickHouse.
|
||
|
||
> **В проде так же, только крупнее.** Здесь один брокер и крошечный срез данных. В бою
|
||
> брокеров несколько, топик разбит на много партиций, читателей в группе — тоже несколько,
|
||
> и Kafka сама делит партиции между ними. Понятия при этом ровно те же, что мы сейчас
|
||
> разглядим на маленьком кластере.
|
||
|
||
---
|
||
|
||
## 2. Наблюдай: открой Kafka UI
|
||
|
||
Подготовь стенд по
|
||
[канонической инструкции курса](../README.md#подготовка-и-канонический-сброс).
|
||
После успешного `world_init` в топиках уже лежат события эталонного мира.
|
||
|
||
Открой Kafka UI:
|
||
`http://localhost:8082`. Ходи по нему свободно — это режим чтения, сломать тут ничего нельзя.
|
||
|
||
Пройди по трём экранам и просто посмотри.
|
||
|
||
**Топики** (раздел *Topics*). Увидишь четыре топика событий — по одному на тип:
|
||
|
||
- `browser_events` — события браузера (pageview и т.п.);
|
||
- `device_events` — про устройство;
|
||
- `geo_events` — гео;
|
||
- `location_events` — местоположение.
|
||
|
||
Рядом могут быть служебные топики:
|
||
|
||
- `__consumer_offsets` — внутренний топик Kafka. В нём Kafka хранит прогресс
|
||
consumer-групп;
|
||
- `generator_state` — слепок состояния генератора на правой границе стартовой истории.
|
||
По нему live-продолжение понимает, откуда продолжать тот же мир;
|
||
- `generator_startup_history_manifest` — паспорт стартовой истории: seed, границы
|
||
модельного времени и контрольные числа;
|
||
- `generator_batch_history` — журнал батчей генератора: сколько сообщений он
|
||
отправил и чем закончилась каждая пачка записи.
|
||
|
||
Служебные топики нужны стенду, но в упражнениях курса мы их не меняем. У каждого нашего
|
||
топика событий в колонке с партициями стоит **1**: топик маленький, делить не на что.
|
||
|
||
**Сообщения в топике.** Открой `browser_events` → вкладку *Messages*. Это и есть события
|
||
стенда. У каждого сообщения видно:
|
||
|
||
- **Offset** — порядковый номер сообщения в партиции (0, 1, 2, …);
|
||
- **Timestamp** — когда сообщение легло в Kafka (время *доставки*, не время самого события);
|
||
- **Value** — тело: JSON события целиком. Например, у сообщения с offset `0`:
|
||
|
||
```json
|
||
{"event_id": "b3336fa4-184d-44e5-9878-98c8f8058496",
|
||
"event_timestamp": "2026-01-01 00:00:00.000000", "event_type": "pageview",
|
||
"click_id": "76a146f2-adc3-4065-8140-9fc42523b554",
|
||
"browser_name": "Opera",
|
||
"browser_user_agent": "Opera/9.44.(Windows NT 5.0; iw-IL) Presto/2.9.183 Version/12.00",
|
||
"browser_language": "ha_NG"}
|
||
```
|
||
|
||
Загляни внутрь Value: у события есть своё `event_timestamp` (когда оно случилось,
|
||
модельное время стенда), и оно отличается от Kafka-Timestamp (когда оно попало в топик).
|
||
Kafka-Timestamp зависит от времени импорта, поэтому у тебя будет своё значение.
|
||
Два разных времени у одной записи — запомни этот момент, в уроке 1 он всплывёт уже на
|
||
стороне ClickHouse.
|
||
|
||
**Consumer-группы** (раздел *Consumers*). Здесь видно, **кто читает** топики. Найдёшь
|
||
четыре группы — по одной на топик:
|
||
|
||
- `ch_stg_browser`, `ch_stg_device`, `ch_stg_geo`, `ch_stg_location`.
|
||
|
||
Это ClickHouse: он подключился к Kafka как читатель и для каждого топика завёл отдельную
|
||
группу. У каждой статус **STABLE** и **1 участник** (member) — тот самый единственный
|
||
consumer ClickHouse.
|
||
|
||
---
|
||
|
||
## 3. Загляни глубже: что значат эти экраны
|
||
|
||
Сложим увиденное в понятия — те же, что в видео, но теперь привязанные к экрану.
|
||
|
||
| Понятие | Что это | Где увидел в Kafka UI |
|
||
|---------|---------|------------------------|
|
||
| **Топик** | именованный поток событий одного типа | `browser_events` и три соседних |
|
||
| **Партиция** | часть топика, внутри которой строгий порядок | у нас по 1 на топик |
|
||
| **Offset** | номер сообщения внутри партиции, по нему адресуют запись | колонка *Offset* в *Messages* |
|
||
| **Consumer-группа** | читатели топика; группа помнит, до какого offset'а дочитали | `ch_stg_*` в разделе *Consumers* |
|
||
| **Lag** | отставание: сколько в топике уже есть, но ещё не прочитано | колонка пустая — см. врезку ниже |
|
||
|
||
Главная мысль: **offset — это адрес сообщения, а не его содержимое**. Пара «партиция +
|
||
offset» однозначно показывает на конкретную запись в топике. Именно по offset'у
|
||
consumer-группа помнит, где остановилась: дочитала до 19 — в следующий раз начнёт с 20,
|
||
а не сначала. Поэтому имя группы — не косметика: смени его, и читатель начнёт топик заново
|
||
(это ты потрогаешь руками в уроке 1, там группа задаётся в DDL как `kafka_group_name`).
|
||
|
||
**Lag** (отставание) — это, по сути, простая арифметика: сколько сообщений в топике уже
|
||
есть, минус сколько группа успела прочитать. Ноль — читатель идёт вровень с потоком; растёт
|
||
— читатель не успевает.
|
||
|
||
> **Маленькая честность про lag.** У групп `ch_stg_*` колонки offset и lag в Kafka UI
|
||
> окажутся **пустыми**, хотя сама группа живая (`STABLE`, 1 участник). Это не баг и не
|
||
> поломка: так уж ClickHouse-движок работает с группой — свой прогресс чтения он держит
|
||
> **на своей стороне** (его видно в системной таблице `system.kafka_consumers`), и именно
|
||
> там стоит смотреть, не отстаёт ли он. Не удивляйся пустой ячейке offset/lag в Kafka UI;
|
||
> проверять отставание ClickHouse удобнее с его конца — этим и займёшься в уроке 1.
|
||
|
||
---
|
||
|
||
## 4. Проверь себя
|
||
|
||
| Действие | Где смотреть | Что ожидать |
|
||
|----------|--------------|-------------|
|
||
| открыть список топиков | Kafka UI → *Topics* | 4 топика событий (`*_events`) и, возможно, служебные топики генератора |
|
||
| открыть `browser_events` | вкладка *Messages* | сообщения с offset'ами 0, 1, 2, …; в Value — JSON события |
|
||
| сравнить два времени | Value (`event_timestamp`) vs Kafka *Timestamp* | время события и время доставки в Kafka различаются |
|
||
| открыть consumers | Kafka UI → *Consumers* | 4 группы `ch_stg_*`, статус `STABLE`, по 1 участнику |
|
||
|
||
---
|
||
|
||
## 5. Что должно получиться
|
||
|
||
После урока у тебя в голове — карта живого кластера: четыре топика событий, в каждом
|
||
сообщения с offset'ами, и четыре consumer-группы ClickHouse, которые их читают. Ничего
|
||
не меняли — только сопоставили слова из видео с экранами Kafka UI.
|
||
|
||
И проверь себя на словах: сможешь объяснить, чем **топик** отличается от **партиции**, что
|
||
такое **offset** и зачем consumer-группе его помнить? Короткая зацепка для ответа — что
|
||
произойдёт, если читатель забудет свой offset и начнёт топик сначала? Примерно такие вопросы
|
||
по теме урока всплывут на еженедельном созвоне.
|
||
|
||
---
|
||
|
||
## Мост к уроку 1
|
||
|
||
Мы посмотрели на поток **со стороны Kafka**: события лежат в топиках, ClickHouse подключён
|
||
к ним группами и что-то вычитывает. Логичный следующий вопрос: **а куда именно ClickHouse
|
||
кладёт прочитанное и как он это делает?** Об этом — урок 1: там ты руками увидишь, как
|
||
сообщение из топика превращается в строку таблицы ClickHouse, и заодно посмотришь на чтение
|
||
Kafka «с другого конца».
|