【Flink】Flink开发环境搭建与WordCount实战

Flink开发环境搭建与WordCount实战

前言

上一篇我们从宏观角度认识了 Flink,知道它是干什么的。但光说不练假把式,这篇文章我们要动手搞起来------从零搭建 Flink 开发环境,并写出人生中第一个 Flink 程序:WordCount(单词计数)

别担心环境问题,我会一步步带你配置,保证你能跑起来。如果中间遇到问题,文末有常见问题汇总。

🏠个人主页:你的主页


目录


一、环境准备清单

在开始之前,先确认你的电脑上有以下环境:

环境 要求 推荐版本 说明
JDK 必须 JDK 8 或 JDK 11 Flink 官方推荐,JDK 17+ 需要额外配置
Maven 必须 3.6+ 依赖管理工具
IDE 推荐 IntelliJ IDEA 社区版免费够用
Git 可选 最新版 版本管理

为什么推荐 JDK 8 或 11?

Flink 对 JDK 版本有一定要求:

  • JDK 8:最稳定,兼容性最好
  • JDK 11:官方支持,长期维护版本
  • JDK 17+:需要额外的 JVM 参数,初学者不推荐

如果你电脑上已经有 JDK,可以用命令查看版本:

bash 复制代码
java -version

输出类似:

复制代码
java version "1.8.0_301"
Java(TM) SE Runtime Environment (build 1.8.0_301-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.301-b09, mixed mode)

看到 1.8 就是 JDK 8,看到 11.x.x 就是 JDK 11。


二、JDK安装与配置

如果你还没安装 JDK,按以下步骤操作:

Windows 用户

  1. 下载 JDK:Oracle JDK 下载AdoptOpenJDK
  2. 双击安装,记住安装路径(如 C:\Program Files\Java\jdk1.8.0_301
  3. 配置环境变量:
    • 新建 JAVA_HOMEC:\Program Files\Java\jdk1.8.0_301
    • Path 中添加:%JAVA_HOME%\bin

Mac 用户

推荐用 Homebrew 安装:

bash 复制代码
# 安装 JDK 8
brew install openjdk@8

# 配置环境变量(添加到 ~/.zshrc 或 ~/.bash_profile)
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
export PATH=$JAVA_HOME/bin:$PATH

# 使配置生效
source ~/.zshrc

Linux 用户

bash 复制代码
# Ubuntu/Debian
sudo apt update
sudo apt install openjdk-8-jdk

# CentOS/RHEL
sudo yum install java-1.8.0-openjdk-devel

# 配置环境变量
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH

三、Maven安装与配置

安装 Maven

Windows
  1. 下载:Maven 官网,选择 apache-maven-x.x.x-bin.zip
  2. 解压到某个目录(如 C:\apache-maven-3.9.5
  3. 配置环境变量:
    • 新建 MAVEN_HOMEC:\apache-maven-3.9.5
    • Path 中添加:%MAVEN_HOME%\bin
Mac
bash 复制代码
brew install maven
Linux
bash 复制代码
# Ubuntu/Debian
sudo apt install maven

# CentOS
sudo yum install maven

验证安装

bash 复制代码
mvn -v

输出类似:

复制代码
Apache Maven 3.9.5
Maven home: /usr/local/Cellar/maven/3.9.5/libexec
Java version: 1.8.0_301, vendor: Oracle Corporation

配置国内镜像(重要!)

Maven 默认从国外下载依赖,速度很慢。强烈建议配置阿里云镜像。

找到 Maven 配置文件 settings.xml

  • Windows:C:\Users\你的用户名\.m2\settings.xml
  • Mac/Linux:~/.m2/settings.xml

如果文件不存在,从 {MAVEN_HOME}/conf/settings.xml 复制一份。

<mirrors> 标签内添加:

xml 复制代码
<mirror>
    <id>aliyunmaven</id>
    <mirrorOf>*</mirrorOf>
    <name>阿里云公共仓库</name>
    <url>https://maven.aliyun.com/repository/public</url>
</mirror>

四、IDEA创建Flink项目

第一步:创建 Maven 项目

  1. 打开 IDEA → FileNewProject
  2. 选择 Maven(不要勾选 Create from archetype)
  3. 填写项目信息:
    • Nameflink-tutorial
    • GroupIdcom.example
    • ArtifactIdflink-tutorial
  4. 点击 Create

第二步:配置 pom.xml

这是最关键的一步。把 pom.xml 替换成以下内容:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>flink-tutorial</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- Flink 版本,推荐使用 1.17.x 或 1.18.x -->
        <flink.version>1.17.2</flink.version>
        <scala.binary.version>2.12</scala.binary.version>
        <slf4j.version>1.7.36</slf4j.version>
    </properties>

    <dependencies>
        <!-- Flink 核心依赖 -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-streaming-java</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- Flink 客户端,本地运行需要 -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-clients</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- 本地运行时需要的运行时依赖 -->
        <dependency>
            <groupId>org.apache.flink</groupId>
            <artifactId>flink-runtime-web</artifactId>
            <version>${flink.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- 日志依赖 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 编译插件 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>

            <!-- 打包插件,用于生成包含依赖的 jar -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.5.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.example.WordCount</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

第三步:刷新 Maven 依赖

在 IDEA 右侧找到 Maven 面板,点击刷新按钮(🔄),等待依赖下载完成。

如果下载很慢,检查是否配置了阿里云镜像。

第四步:配置运行参数

因为 Flink 依赖是 provided 作用域(生产环境由集群提供),本地运行需要额外配置:

  1. 点击 RunEdit Configurations
  2. 选择你的运行配置
  3. 勾选 Include dependencies with "Provided" scope

或者直接在 pom.xml 中把 <scope>provided</scope> 改成 <scope>compile</scope>(仅开发阶段)。


五、第一个WordCount程序

创建代码目录结构

复制代码
src/main/java/com/example/
└── WordCount.java

WordCount.java 完整代码

java 复制代码
package com.example;

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.util.Collector;

/**
 * Flink 入门程序:WordCount(单词计数)
 * 
 * 功能:统计输入文本中每个单词出现的次数
 * 
 * @author 山沐与山
 */
public class WordCount {

    public static void main(String[] args) throws Exception {
        
        // ============== 第一步:创建执行环境 ==============
        // 这是所有 Flink 程序的入口,类似于 SparkContext
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        
        // ============== 第二步:读取数据源(Source) ==============
        // 这里我们用一个简单的集合作为数据源
        // 实际生产中,数据源通常是 Kafka、文件、数据库等
        DataStream<String> textStream = env.fromElements(
            "hello flink",
            "hello world",
            "flink is awesome",
            "hello flink world"
        );
        
        // ============== 第三步:数据转换(Transformation) ==============
        DataStream<Tuple2<String, Integer>> wordCountStream = textStream
            // 3.1 将每行文本切分成单词,并转换为 (单词, 1) 的形式
            .flatMap(new Tokenizer())
            // 3.2 按单词分组(keyBy)
            .keyBy(tuple -> tuple.f0)
            // 3.3 对每组数据求和
            .sum(1);
        
        // ============== 第四步:输出结果(Sink) ==============
        // 这里直接打印到控制台,实际生产中会写入 Kafka、数据库等
        wordCountStream.print("WordCount");
        
        // ============== 第五步:触发执行 ==============
        // Flink 是懒执行的,只有调用 execute() 才会真正开始运行
        env.execute("Flink WordCount Job");
    }
    
    /**
     * 自定义 FlatMapFunction:将一行文本切分成多个单词
     * 
     * 输入:一行文本,如 "hello flink"
     * 输出:多个 (单词, 1) 元组,如 (hello, 1), (flink, 1)
     */
    public static class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> {
        
        @Override
        public void flatMap(String line, Collector<Tuple2<String, Integer>> out) {
            // 按空格切分
            String[] words = line.toLowerCase().split("\\s+");
            
            // 遍历每个单词,输出 (单词, 1)
            for (String word : words) {
                if (word.length() > 0) {
                    out.collect(new Tuple2<>(word, 1));
                }
            }
        }
    }
}

六、运行与验证

在 IDEA 中运行

  1. 右键点击 WordCount.java
  2. 选择 Run 'WordCount.main()'

预期输出

复制代码
WordCount:3> (awesome,1)
WordCount:7> (flink,3)
WordCount:6> (world,2)
WordCount:1> (hello,3)
WordCount:4> (is,1)

输出解释:

  • WordCount:3> 表示这条数据由第3个并行任务处理
  • (flink,3) 表示单词 "flink" 出现了 3 次

恭喜你! 你的第一个 Flink 程序已经成功运行了!

可能遇到的问题

如果报错 ClassNotFoundException,说明 Flink 依赖没有正确加载。检查:

  1. Maven 依赖是否下载完成
  2. 是否配置了 Include dependencies with "Provided" scope
  3. 或者把 pom.xml 中的 provided 改成 compile

七、代码逐行解析

让我们把代码拆开来,一块块理解:

7.1 创建执行环境

java 复制代码
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

这行代码是所有 Flink 流处理程序的起点。StreamExecutionEnvironment 可以理解为 Flink 的"总管家",它负责:

  • 配置并行度、状态后端等参数
  • 管理数据流的整个生命周期
  • 最终提交作业到集群执行

类比一下:如果 Flink 程序是一部电影,env 就是导演。

7.2 读取数据源

java 复制代码
DataStream<String> textStream = env.fromElements(
    "hello flink",
    "hello world",
    // ...
);

fromElements() 是最简单的数据源,直接从内存中的集合读取数据。

常用的数据源还有:

方法 说明 场景
fromElements() 从集合读取 测试、演示
fromCollection() 从 List 读取 测试
readTextFile() 从文件读取 批处理
socketTextStream() 从 Socket 读取 实时测试
addSource(new FlinkKafkaConsumer()) 从 Kafka 读取 生产环境

7.3 数据转换

这是 Flink 程序的核心部分:

java 复制代码
textStream
    .flatMap(new Tokenizer())  // 切分单词
    .keyBy(tuple -> tuple.f0)  // 按单词分组
    .sum(1);                   // 求和

flatMap:一对多的转换。一行文本 → 多个 (单词, 1) 元组

复制代码
输入:"hello flink"
输出:(hello, 1), (flink, 1)

keyBy:按 key 分组。相同 key 的数据会被发送到同一个 Task 处理

复制代码
所有 key="hello" 的数据 → Task A
所有 key="flink" 的数据 → Task B

sum(1):对 Tuple 的第 2 个字段(索引为 1)求和

复制代码
(hello, 1) + (hello, 1) + (hello, 1) = (hello, 3)

7.4 数据流转换过程图解

复制代码
原始数据流
    │
    │  "hello flink"
    │  "hello world"
    │  "flink is awesome"
    │  "hello flink world"
    │
    ▼
┌─────────────────────────────────────────────────────────┐
│ flatMap(Tokenizer)                                      │
│                                                         │
│  "hello flink"  →  (hello,1), (flink,1)                │
│  "hello world"  →  (hello,1), (world,1)                │
│  "flink is awesome" → (flink,1), (is,1), (awesome,1)   │
│  "hello flink world" → (hello,1), (flink,1), (world,1) │
└─────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────┐
│ keyBy(word)                                             │
│                                                         │
│  hello组:  (hello,1), (hello,1), (hello,1)            │
│  flink组:  (flink,1), (flink,1), (flink,1)            │
│  world组:  (world,1), (world,1)                       │
│  is组:     (is,1)                                     │
│  awesome组:(awesome,1)                                │
└─────────────────────────────────────────────────────────┘
    │
    ▼
┌─────────────────────────────────────────────────────────┐
│ sum(1)                                                  │
│                                                         │
│  (hello, 3)                                            │
│  (flink, 3)                                            │
│  (world, 2)                                            │
│  (is, 1)                                               │
│  (awesome, 1)                                          │
└─────────────────────────────────────────────────────────┘

7.5 触发执行

java 复制代码
env.execute("Flink WordCount Job");

这行代码非常关键!Flink 程序是懒执行 的,前面所有的代码只是在构建执行计划(DAG),只有调用 execute() 才会真正开始执行。

这和 Spark 的 action 操作很像。


八、常见问题与解决

Q1:报错 java.lang.NoClassDefFoundError

原因:Flink 依赖没有正确加载

解决

  1. 在 IDEA 运行配置中勾选 Include dependencies with "Provided" scope
  2. 或者把 pom.xml 中的 <scope>provided</scope> 改成 <scope>compile</scope>

Q2:Maven 下载依赖很慢

原因:默认使用国外仓库

解决:配置阿里云镜像(见第三节)

Q3:报错 Could not find or load main class

原因:包路径不对

解决 :确保 WordCount.java 的 package 声明和实际目录结构一致

Q4:运行后没有输出

原因:可能是日志级别问题

解决 :在 src/main/resources 下创建 log4j.properties

properties 复制代码
log4j.rootLogger=INFO, console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %-60c %x - %m%n

Q5:JDK 版本不兼容

原因:使用了 JDK 17+

解决

  1. 切换到 JDK 8 或 11
  2. 或者添加 JVM 参数:--add-opens java.base/java.lang=ALL-UNNAMED

九、总结

这篇文章我们完成了:

  1. 环境准备:JDK 8/11 + Maven + IDEA
  2. 项目配置:创建 Maven 项目,配置 Flink 依赖
  3. 第一个程序:WordCount 单词计数
  4. 代码理解
    • StreamExecutionEnvironment:程序入口
    • Source:数据从哪来
    • Transformation:数据怎么处理(flatMap、keyBy、sum)
    • Sink:数据到哪去
    • execute():触发执行

核心知识点

概念 说明
DataStream Flink 中的数据流抽象
flatMap 一对多转换
keyBy 按 key 分组,相同 key 的数据去同一个 Task
sum 聚合操作,对指定字段求和
懒执行 必须调用 execute() 才会真正运行

下一篇文章,我们将深入剖析 Flink 的架构,看看 JobManager 和 TaskManager 是怎么协作的,理解 Flink 是如何把任务分配到多台机器上并行执行的。


热门专栏推荐

等等等还有许多优秀的合集在主页等着大家的光顾,感谢大家的支持


文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😊

希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🙏

如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🌟

相关推荐
图导物联4 小时前
厂区地图导航系统:基于北斗/GPS+蓝牙 iBeacon 的开发方案,破解 “定位不准、调度混乱、安全薄弱” 三大痛点
大数据·人工智能·物联网
PPIO派欧云4 小时前
PPIO上线Prompt Cache:让模型调用更快、更省、更稳
大数据·人工智能·prompt
Q_Q5110082854 小时前
python_django基于大数据技术旅游景点数据分析推荐系统现_wrqk1aes
大数据·python·django
zhixingheyi_tian4 小时前
Hadoop 之 ENV
大数据·hadoop·分布式
小鹿学程序4 小时前
任务一- 2.子任务二:Hadoop完全分布式安装配置
大数据·hadoop·分布式
未来之窗软件服务9 小时前
一体化系统(九)智慧社区综合报表——东方仙盟练气期
大数据·前端·仙盟创梦ide·东方仙盟·东方仙盟一体化
火星资讯12 小时前
Zenlayer AI Gateway 登陆 Dify 市场,轻装上阵搭建 AI Agent
大数据·人工智能
星海拾遗13 小时前
git rebase记录
大数据·git·elasticsearch
Elastic 中国社区官方博客13 小时前
Elasticsearch:在分析过程中对数字进行标准化
大数据·数据库·elasticsearch·搜索引擎·全文检索