Maven 配置
<properties>
<maven.compiler.release>17</maven.compiler.release>
<flink.version>1.20.0</flink.version>
</properties>
<dependencies>
<!-- Flink 核心 -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients</artifactId>
<version>${flink.version}</version>
</dependency>
<!-- Kafka 连接器 -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka</artifactId>
<version>3.3.0-1.20</version>
</dependency>
<!-- CEP -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-cep</artifactId>
<version>${flink.version}</version>
</dependency>
<!-- RocksDB 状态后端(用到 EmbeddedRocksDBStateBackend 时必须显式引入) -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-statebackend-rocksdb</artifactId>
<version>${flink.version}</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-test-utils</artifactId>
<version>${flink.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
注意两点:
flink-connector-kafka的版本号3.3.0-1.20中,3.3.0是 connector 版本,1.20是 Flink 版本。必须匹配你的 Flink 版本。maven.compiler.release用 17。Flink 1.20 只支持 Java 11 和 17,用 21 或 25 编译虽然能过,运行时不受支持。
第一个 Flink 作业
经典的 WordCount,用 DataStream API:
import java.time.Duration;
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.TumblingProcessingTimeWindows;
import org.apache.flink.util.Collector;
public class WordCount {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
// 设置并行度(默认 = CPU 核心数)
env.setParallelism(2);
// Source:从 socket 读取(开发测试用)
DataStream<String> text = env.socketTextStream("localhost", 9999);
// Transformation:分词 + 计数
DataStream<Tuple2<String, Integer>> wordCounts = text
.flatMap(new Tokenizer())
.keyBy(value -> value.f0)
.window(TumblingProcessingTimeWindows.of(Duration.ofSeconds(5)))
.sum(1);
// Sink:打印到控制台
wordCounts.print();
// 启动作业
env.execute("Socket WordCount");
}
static class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
for (String word : value.split(" ")) {
out.collect(new Tuple2<>(word.toLowerCase(), 1));
}
}
}
}
运行方式
方式 1:本地 MiniCluster(开发)
# 启动 netcat 发送数据
nc -lk 9999
# 另开终端运行作业
mvn compile exec:java -Dexec.mainClass="WordCount"
# 在 nc 终端输入:
hello world
hello flink
hello java
# 控制台输出:
# (hello, 3)
# (world, 1)
# (flink, 1)
# (java, 1)
getExecutionEnvironment() 在 IDE 里返回 LocalStreamEnvironment:它在同一个 JVM 里用多线程执行作业,不需要外部集群。它不是 MiniCluster——没有独立 TaskManager 进程,也不能通过它验证提交、Slot 分配或跨进程网络。需要这些能力时,用下一节的显式 MiniCluster。
方式 2:显式 MiniCluster(测试)
import org.apache.flink.runtime.testutils.MiniClusterResourceConfiguration;
import org.apache.flink.test.util.MiniClusterWithClientResource;
public class FlinkTest {
@ClassRule
public static MiniClusterWithClientResource cluster =
new MiniClusterWithClientResource(
new MiniClusterResourceConfiguration.Builder()
.setNumberSlotsPerTaskManager(2)
.setNumberTaskManagers(1)
.build());
}
JUnit 4 的 @ClassRule 在测试类开始前启动 MiniCluster,测试结束后自动关闭。MiniClusterWithClientResource 在 1.20 仍可用;JUnit 5 项目改用 org.apache.flink.test.junit5.MiniClusterExtension,它会把启动、关闭和执行上下文一起管好,写法见 13.2
。
StreamExecutionEnvironment 配置
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 并行度
env.setParallelism(4);
// 时间语义:由每个 Source 的 WatermarkStrategy 决定,没有全局开关
// Checkpoint 配置(第 5.3 节详细展开)
env.enableCheckpointing(60000); // 每 60 秒 Checkpoint,语义默认 EXACTLY_ONCE
// 重启策略
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3, Duration.ofSeconds(10)));
// 状态后端
env.setStateBackend(new HashMapStateBackend());
env.getCheckpointConfig().setCheckpointStorage("file:///tmp/flink-checkpoints");
常见问题
| 问题 | 原因 | 解决 |
|---|---|---|
NoResourceAvailableException | Task slot 不足(并行度大于可用 slot 数) | 降低并行度,或增加 TaskManager/slot;不要靠调大并行度解决 |
ClassNotFoundException: KafkaSource | connector 版本不匹配 | 确认 flink-connector-kafka 版本与 Flink 版本匹配 |
| 输出乱序 | 使用了 Processing Time | 改用 Event Time + Watermark |
| 作业不退出 | execute() 阻塞主线程 | 正确,流作业是长期运行的;测试用 env.executeAsync() |
| 本地端口冲突 | MiniCluster 默认端口被占 | 修改 RestOptions.PORT |
Flink Web UI
本地运行时,Flink 启动 Web UI(默认端口 8081):
http://localhost:8081
可查看:
- 作业 DAG(数据流拓扑图)
- 各算子的背压状态
- Checkpoint 历史
- 任务日志
eventd v3 的 Flink 配置
eventd-v3 的 analytics 模块配置:
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(4);
env.enableCheckpointing(30000);
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(10000);
env.getCheckpointConfig().setCheckpointTimeout(60000);
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(5, Duration.ofSeconds(30)));
env.setStateBackend(new EmbeddedRocksDBStateBackend());
env.getCheckpointConfig().setCheckpointStorage("file:///var/flink/checkpoints");
| 参数 | 值 | 原因 |
|---|---|---|
parallelism | 4 | 开发机 4 核 |
checkpointing interval | 30s | 平衡容错与性能 |
minPauseBetweenCheckpoints | 10s | 防止 Checkpoint 过于密集 |
maxConcurrentCheckpoints | 1 | 顺序执行,减少 I/O 压力 |
restart attempts | 5 | 短暂故障自动恢复 |
state backend | RocksDB | 大状态(窗口内数据多)时内存安全 |
小结
| 概念 | 说明 |
|---|---|
StreamExecutionEnvironment | 作业入口,配置全局参数 |
getExecutionEnvironment() | 自动检测环境(本地/集群) |
setParallelism() | 全局并行度 |
enableCheckpointing() | 开启容错快照 |
setStateBackend() | 状态存储后端 |
| MiniCluster | 本地测试无需外部集群 |
| Web UI | localhost:8081 查看作业状态 |
第 1 章结束。下一节 2.1 Kafka Streams 拓扑 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。