Kafka Streams: обработка потоков
Урок про то, как считать прямо над потоком — фильтровать, агрегировать, джойнить — не разворачивая отдельный кластер вычислений.
Kafka Streams — клиентская библиотека для обработки потоков: читает из топиков, преобразует данные (map/filter/агрегации/джойны/окна) и пишет результат обратно в топики, работая как обычное приложение.
Зачем это нужно
Брокер только хранит и доставляет; вычисления над потоком — отдельная задача. Kafka Streams встраивает её прямо в ваше приложение: не нужен отдельный движок вроде Flink или Spark, достаточно подключить библиотеку. Вы пишете обычный сервис, который масштабируется и отказоустойчив за счёт самой Kafka.
KStream против KTable
| KStream | KTable | |
| Это | поток событий (каждая запись — факт) | таблица состояний (ключ -> текущее значение) |
| Новая запись по ключу | ещё одно событие | обновление значения ключа |
| Аналогия | журнал транзакций | результат компакции лога |
Двойственность «поток-таблица» — сердце Streams: поток фактов можно свернуть в таблицу состояний (агрегация), а таблицу — развернуть обратно в поток изменений.
Операции
clicks (KStream)
.filter(событие -> событие.valid)
.groupByKey()
.windowedBy(5 минут)
.count() -> KTable: кликов по ключу за окно
.toStream()
.to("clicks-per-5min")
Здесь поток кликов фильтруется, группируется по ключу и считается в окнах по 5 минут. Окна нужны, потому что поток бесконечен: «сколько всего» не имеет конца, а «сколько за последние 5 минут» — конкретный ответ.
Джойны и stateful-обработка
Streams умеет джойнить потоки между собой и поток с таблицей: обогатить событие заказа данными о пользователе (orders.join(users)). Такие операции stateful — им нужно помнить состояние (накопленные счётчики, последние значения ключей). Streams хранит это состояние в локальном встроенном хранилище (RocksDB) и одновременно зеркалит его в compacted-топик Kafka.
Как работает под капотом
Параллелизм Streams наследуется от партиций входных топиков: задачи Streams привязаны к партициям, поэтому запустить можно столько экземпляров приложения, сколько партиций. Локальное состояние (RocksDB на диске инстанса) дублируется в changelog-топик с компакцией — это делает stateful-обработку отказоустойчивой: упал инстанс, его партиции и состояние подхватит другой, восстановив RocksDB-хранилище из changelog. По сути Streams строит exactly-once конвейер поверх транзакций Kafka из предыдущего раздела.
Частые ошибки
- Агрегировать без окон. «Сумма за всё время» по бесконечному потоку растёт неограниченно; задавайте окна.
- Джойнить потоки с разным ключом партиционирования. Для джойна данные должны быть co-partitioned (одинаковый ключ и число партиций).
- Игнорировать changelog-топик. Без него локальное состояние не восстановить после падения инстанса.
Итоги
- Kafka Streams считает над потоком прямо в приложении: filter, агрегации, джойны, окна.
- KStream — поток фактов, KTable — таблица состояний; они взаимно преобразуемы.
- Stateful-состояние живёт в RocksDB и зеркалится в changelog-топик ради отказоустойчивости.