Unravel › Java concepts › Messaging and Kafka
Kafka Streams
Kafka Streams is a library for processing topics as continuous streams inside your own application: filter, transform, join and aggregate events, with state kept locally and backed up to Kafka.
Learn these first
At a glance
A sorting line built from a recipe.
- 1You describe a topology with StreamsBuilder: builder.stream("orders") reads a topic as a KStream (every event is a fact), builder.table("customers") reads it as a KTable (the latest value per key)
- 2Stateless steps: filter, map, flatMap, branchStateful steps: groupByKey().count(), aggregate, windowed counts (windowedBy(TimeWindows...)) and joins between streams and tables.
- 3The result is written with to("output-topic")The application id doubles as the consumer group, so running more instances automatically splits partitions between them.
- 4State lives in local RocksDB stores plus a changelog topic in KafkaAfter a crash or move, another instance rebuilds the store by replaying the changelog.
- Joins need co-partitioning: both topics must have the same number of partitions and the same key strategy, otherwise matching records land on different instances and never meet
- The topology is split into sub-topologies, each turned into tasks (one per input partition)
StreamsBuilder builder = new StreamsBuilder();KStream<String, String> orders = builder.stream("orders");orders.filter((k, v) -> v != null && !v.isEmpty()) .groupByKey() .windowedBy(TimeWindows.ofSizeWithNoGrace(Duration.ofMinutes(5))) .count() .toStream() .map((window, count) -> KeyValue.pair(window.key(), String.valueOf(count))) .to("orders-per-5-minutes");- line 1 uses StreamsBuilder, the core of Kafka Streams
- line 2 uses KStream, the core of Kafka Streams
- line 4 uses groupByKey, the core of Kafka Streams
- line 5 uses windowedBy, the core of Kafka Streams
- line 8 uses KeyValue, the core of Kafka Streams
Read the full explanation (about 2 min)
Kafka Streams is a library for processing topics as continuous streams inside your own application: filter, transform, join and aggregate events, with state kept locally and backed up to Kafka.
A mental model
A sorting line built from a recipe. Events flow past, each station (filter, map, count) does its small job, and the running totals are kept in a notebook at the station that is also copied to a safe in the warehouse in case the station burns down.
How it works
You describe a topology with StreamsBuilder: builder.stream("orders") reads a topic as a KStream (every event is a fact), builder.table("customers") reads it as a KTable (the latest value per key).
Stateless steps: filter, map, flatMap, branch. Stateful steps: groupByKey().count(), aggregate, windowed counts (windowedBy(TimeWindows...)) and joins between streams and tables.
The result is written with to("output-topic"). The application id doubles as the consumer group, so running more instances automatically splits partitions between them.
State lives in local RocksDB stores plus a changelog topic in Kafka. After a crash or move, another instance rebuilds the store by replaying the changelog.
Going deeper
Joins need co-partitioning: both topics must have the same number of partitions and the same key strategy, otherwise matching records land on different instances and never meet.
Time matters. Windows use event time from the record timestamp, late events are accepted only within the grace period, and suppress emits a final result per window instead of every update.
Exactly-once is one setting away (processing.guarantee=exactly_once_v2), at the cost of transactions and slightly higher latency. Without it, processing is at-least-once and results can be double counted after a failure.
Under the hood
The topology is split into sub-topologies, each turned into tasks (one per input partition). Tasks are assigned to stream threads in instances through the consumer group protocol. Each task owns its state stores and processes records one at a time on its thread; a repartition topic is inserted automatically when the key changes.
Common mistakes
- Joining topics that are not co-partitioned.
- Changing the key and forgetting that a repartition topic costs a round trip.
- Ignoring late and out-of-order events in windows.
- Changing the topology of a running application with state without resetting it.
How Unravel recognises it
When you paste code, Unravel looks for these signs of kafka streams. Spotting them yourself is a good habit when you read unfamiliar code:
- Annotations:
@EnableKafkaStreams - Types:
StreamsBuilderKStreamKTableGlobalKTableKafkaStreamsTopologyStreamsConfigMaterializedTimeWindowsSessionWindowsGroupedProducedConsumedJoinedand 9 more - Method calls:
groupByKey()windowedBy()
Part of a learning path
- Kafka beyond the basics · previous: Kafka idempotent producer and transactions · next: Schema Registry, Avro and Protobuf
Related concepts
Frequently asked questions
What is Kafka Streams?
Kafka Streams is a library for processing topics as continuous streams inside your own application: filter, transform, join and aggregate events, with state kept locally and backed up to Kafka.
What are the most common mistakes with Kafka Streams?
Joining topics that are not co-partitioned. Changing the key and forgetting that a repartition topic costs a round trip. Ignoring late and out-of-order events in windows. Changing the topology of a running application with state without resetting it.
How can I see Kafka Streams in real code?
Paste your own Java into Unravel and it will point out where kafka streams appears, explain how it works at the detail level you choose, and draw the flow of the code around it.