4. Flink基础入门:核心概念、DataStream API、环境搭建与第一个实时任务

好,咱们进入第四章。这一章是地基中的地基。你想想看,一个实时推荐平台,数据像流水一样哗哗地进来,你得有个东西能接得住、算得快、还不丢数据。Flink 就是干这个的。

我记得我刚接触 Flink 那会儿,第一反应是:这玩意儿跟 Spark Streaming 有啥区别?后来踩了几个坑才明白,Flink 的「真·实时」和「精确一次」语义,在推荐场景下简直是天作之合。今天我就把当年摸索出来的东西,掰开了揉碎了讲给你。

4.1 Flink 核心概念:你必须要搞懂的三个东西

Flink 的概念其实不多,但每个都很关键。我个人习惯把核心抽象归纳为三层:流、算子、状态。

概念 一句话解释 我踩过的坑
DataStream 无限流动的数据集合,有界/无界 一开始总把无界流当批处理写,结果 watermark 没设对,窗口永远不触发
算子(Operator) map、filter、keyBy、window 等转换逻辑 keyBy 之后没做 rebalance,数据倾斜导致 OOM
状态(State) 算子内部保存的中间数据,支持容错 忘了开 checkpoint,任务挂了全量重算,被老板骂了一顿

说白了,Flink 就是一个「数据管道工厂」。你把数据源接在入口,把处理逻辑串成管道,把结果送到出口。中间所有「记住的东西」就是状态。

核心要点:Flink 的「有状态计算」是它跟普通消息队列处理最大的区别。没有状态,你只能做无脑过滤;有了状态,你才能做累计、去重、关联、窗口聚合——推荐系统里 80% 的逻辑都依赖状态。

4.2 DataStream API:你写 Flink 任务的主要战场

DataStream API 是 Flink 对开发者暴露的核心编程接口。我建议你把它想象成 Java 8 的 Stream API 的「实时加强版」。

4.2.1 核心算子速览

  • Source:数据从哪来?Kafka、Socket、文件、自定义 Source。
  • Transformation:数据怎么变?map、flatMap、filter、keyBy、window、process。
  • Sink:数据往哪去?Kafka、MySQL、Elasticsearch、Redis、自定义 Sink。

嗯,这里要注意:keyBy 不是分组,是分区。它决定了相同 key 的数据进入同一个 subtask,这是后续 window 和 state 能正确工作的前提。

我的小技巧:写 Flink 任务时,先把 Source 和 Sink 写好,中间用 .print() 代替 Sink 调试。等逻辑跑通了,再换成真正的 Sink。这样能省一半的排错时间。

4.2.2 一个完整的 DataStream 任务骨架

// 1. 创建执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

// 2. 设置 checkpoint(我建议生产环境必开)
env.enableCheckpointing(5000); // 每 5 秒一次

// 3. 定义数据源
DataStream<String> sourceStream = env.socketTextStream("localhost", 9999);

// 4. 转换逻辑
DataStream<WordCount> wordCounts = sourceStream
    .flatMap(new Tokenizer())
    .keyBy(word -> word.f0)
    .window(TumblingProcessingTimeWindows.of(Time.seconds(10)))
    .sum(1);

// 5. 输出结果
wordCounts.print();

// 6. 启动任务
env.execute("My First Flink Job");

这段代码我当年写了不下 50 遍。你注意看,env.execute() 是必须的,不调用它 Flink 不会真正启动。我曾经调试了半天发现没输出,结果就是忘了这行。

4.3 环境搭建:本地跑起来再说

我建议你从本地模式开始。别一上来就搞集群,先让代码在 IDE 里跑通,心里有底了再上分布式。

4.3.1 本地模式(推荐新手)

  1. 下载 Flink 二进制包(我习惯用 1.13+ 版本)
  2. 解压后运行 ./bin/start-cluster.sh
  3. 访问 http://localhost:8081 看 Web UI
  4. 在 IDE 里写代码,直接 Run 就行

避坑指南:我曾经在本地跑任务时,发现 Web UI 上一直显示「No jobs running」。查了半天,原来是 IDE 里默认用了 setParallelism(1),而 Web UI 只显示分布式执行的任务。解决办法:在 env.execute() 之前不要手动设并行度,或者设成大于 1。

4.3.2 集群模式(生产环境)

生产环境我推荐用 Standalone 或 YARN 模式。核心步骤就三步:

  • 配置 flink-conf.yaml(jobmanager.rpc.address、taskmanager.numberOfTaskSlots 等)
  • 分发 Flink 包到所有节点
  • 启动集群:./bin/start-cluster.sh

你想想看,本地和集群的区别其实就是「谁来分配资源」。本地是 JVM 自己管,集群是 YARN/K8s 管。代码本身几乎不用改。

4.4 第一个 Flink 实时任务:从 Socket 读取并实时统计

好,咱们动手写一个。这个任务虽然简单,但包含了 Flink 实时任务的所有核心要素。

4.4.1 任务目标

从 Socket 端口读取文本流,每隔 10 秒统计一次单词出现次数,输出到控制台。

4.4.2 完整代码

public class SocketWordCount {
    public static void main(String[] args) throws Exception {
        // 环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(2);

        // 数据源:监听 9999 端口
        DataStream<String> text = env.socketTextStream("localhost", 9999);

        // 转换:分词 -> 计数
        DataStream<Tuple2<String, Integer>> counts = text
            .flatMap((String line, Collector<Tuple2<String, Integer>> out) -> {
                for (String word : line.split("\\s+")) {
                    out.collect(new Tuple2<>(word, 1));
                }
            })
            .returns(Types.TUPLE(Types.STRING, Types.INT))
            .keyBy(value -> value.f0)
            .window(TumblingProcessingTimeWindows.of(Time.seconds(10)))
            .sum(1);

        // 输出
        counts.print();

        // 执行
        env.execute("Socket Word Count");
    }
}

4.4.3 运行步骤

  1. 启动 Flink 本地集群(或 IDE 直接运行)
  2. 在终端执行 nc -lk 9999 启动 Socket 服务
  3. 在 IDE 里运行上面的代码
  4. 在终端输入单词,比如 hello flink hello world
  5. 观察控制台输出:每 10 秒打印一次统计结果

调试技巧:如果没输出,先检查 nc 是否正常,再检查 Flink Web UI 上任务是否在运行。我遇到过最蠢的坑是防火墙没关 9999 端口。

4.5 本章知识体系总览

下面这张图是我自己总结的 Flink 入门知识结构,你照着这个脉络学,不会迷路。

Flink 入门知识体系 Flink 入门知识体系 核心概念 DataStream / 算子 / 状态 DataStream API Source / Transformation / Sink 环境搭建 本地模式 / 集群模式 第一个实时任务:Socket WordCount 1. 创建执行环境 2. 定义 Source 3. 转换逻辑 4. 输出 & 执行 💡 学习路径建议 先理解核心概念 → 再动手写 DataStream API → 最后搭建环境跑第一个任务

这张图把本章的四个模块串起来了。你从左上角的核心概念开始,顺着箭头走到右下角的任务执行,每一步都是下一环的基础。

总结一下:Flink 入门其实就三件事——搞懂流和状态、学会 DataStream API、把环境搭起来跑通第一个任务。别贪多,先把这三点吃透,后面的窗口、时间语义、状态后端、容错机制都是锦上添花。