Kafka Streams
Kafka Streams 是 Kafka 官方的流处理库,作为一个 Java 库直接嵌入应用,无需独立的流处理集群(与 Flink、Spark Streaming 不同)。
架构
Kafka Cluster
┌─────────────────────┐
│ input-topic │
│ (原始数据) │
└────────┬────────────┘
│
┌────────▼────────────┐
│ Kafka Streams App │
│ ┌───────────────┐ │
│ │ Stream DSL │ │
│ │ Processor API │ │
│ └───────────────┘ │
│ (State Store) │
└────────┬────────────┘
│
┌────────▼────────────┐
│ output-topic │
│ (处理结果) │
└─────────────────────┘| 特性 | 说明 |
|---|---|
| 无外部依赖 | 仅依赖 Kafka 集群 |
| 有状态/无状态 | 本地 RocksDB State Store + 可恢复(Changelog Topic) |
| Exactly-once | 天然支持 EOS 语义 |
| 弹性伸缩 | 应用实例增减自动 Rebalance |
| KSQL | 流式 SQL 引擎,对标 Flink SQL |
Stream DSL 示例
java
Properties props = new Properties();
props.put(StreamsConfig.APPLICATION_ID_CONFIG, "order-processor");
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Serdes.String().getClass());
StreamsBuilder builder = new StreamsBuilder();
// 数据流: input-topic → 过滤 → 转换 → 分组 → 聚合 → output-topic
KStream<String, String> source = builder.stream("raw-orders");
source
.filter((key, orderJson) -> isValidOrder(orderJson)) // 过滤无效订单
.mapValues(orderJson -> extractOrderAmount(orderJson)) // 提取金额
.groupBy((key, amount) -> getRegion(amount), // 按区域分组
Grouped.with(Serdes.String(), Serdes.Double()))
.aggregate(
() -> 0.0,
(region, amount, total) -> total + amount, // 累加金额
Materialized.<String, Double, KeyValueStore<Bytes, byte[]>>as("region-sales")
.withValueSerde(Serdes.Double()))
.toStream()
.to("region-sales-output", Produced.with(Serdes.String(), Serdes.Double()));
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();
Runtime.getRuntime().addShutdownHook(new Thread(streams::close));KSQL
KSQL 是 Kafka Streams 的 SQL 引擎,无需写 Java 代码即可对流数据进行查询和处理。
sql
-- 创建 Stream(无界数据流)
CREATE STREAM orders (
order_id VARCHAR,
user_id VARCHAR,
amount DOUBLE,
region VARCHAR
) WITH (
KAFKA_TOPIC = 'orders',
VALUE_FORMAT = 'JSON'
);
-- 过滤
CREATE STREAM high_value_orders AS
SELECT * FROM orders WHERE amount > 1000;
-- 聚合(窗口计算)
CREATE TABLE region_sales AS
SELECT region,
SUM(amount) AS total_sales,
COUNT(*) AS order_count
FROM orders
WINDOW TUMBLING (SIZE 1 MINUTE)
GROUP BY region;
-- 查询
SELECT * FROM high_value_orders EMIT CHANGES;
SELECT * FROM region_sales EMIT CHANGES;| Stream 与 Table 对比 | Stream | Table |
|---|---|---|
| 语义 | 追加日志(事件流) | 变更日志(当前状态) |
| 查询 | 每次返回新事件 | 返回当前最新值 |
| 存储 | 不持久化 | 本地 State Store + Changelog Topic |
有状态操作
| 操作 | 说明 | 状态存储 |
|---|---|---|
aggregate | 聚合(Sum/Count/Reduce) | 累加值 |
join | 流-流 或 流-表 Join | 窗口数据 |
count | 计数 | 计数值 |
reduce | 归约 | 归约结果 |
java
// Windowed Join — 按时间窗口 Join 两个 Stream
KStream<String, Order> orders = builder.stream("orders");
KStream<String, Payment> payments = builder.stream("payments");
orders.join(payments,
(order, payment) -> new EnrichedOrder(order, payment),
JoinWindows.ofTimeDifferenceWithNoGrace(Duration.ofMinutes(5)),
StreamJoined.with(Serdes.String(), orderSerde, paymentSerde))
.to("enriched-orders");核心配置
| 参数 | 说明 |
|---|---|
APPLICATION_ID_CONFIG | 应用 ID,同 Group 共享 State Store |
NUM_STREAM_THREADS_CONFIG | 线程数,匹配 Partition 数 |
STATE_DIR_CONFIG | 本地状态存储目录 |
CACHE_MAX_BYTES_BUFFERING_CONFIG | 状态缓存大小 |
PROCESSING_GUARANTEE_CONFIG | exactly_once_v2(推荐)或 at_least_once |
DEFAULT_KEY_SERDE_CLASS_CONFIG | 默认 Key 序列化器 |
DEFAULT_VALUE_SERDE_CLASS_CONFIG | 默认 Value 序列化器 |