一、为什么很多人需要自己编译 Flink 源码?
先说一个很现实的问题:
已经有官方二进制包了,为什么还要自己编译?
原因通常有下面几种。
1. 想读源码、打断点调试
如果你要研究 Flink 内部执行流程,比如:
- JobGraph 如何生成
- Checkpoint 如何触发
- Task 是怎么运行的
- 调度和 failover 是怎么实现的
那么只看源码还不够,很多时候你必须能在本地把工程编译起来,甚至跑起来。
2. 想做二次开发
例如你可能想:
- 修改某个 connector
- 调整某段 runtime 逻辑
- 增加公司内部定制能力
- 改造提交流程或运行机制
这时候,自己编译源码就是基础能力。
3. 想参与社区开发
如果你打算给 Flink 提 PR,源码编译几乎是第一道门槛。
你必须保证自己本地能够完成构建,否则后面的调试、改动、测试都很难继续。
4. 想打包 PyFlink
很多人一开始只关注 Java / Scala 侧,但当你需要构建可安装的 PyFlink 包时,源码编译也绕不开。
所以说,Flink 源码编译并不是"高级玩法",而是深入学习和开发 Flink 的基本功。
二、编译 Flink 2.2.0 需要什么环境?
在正式开始之前,先把基础环境准备好。
这一步看着简单,但其实决定了你后面会不会疯狂踩坑。
1. 源码
你需要先拿到 Flink 源码。
可以下载 release source,也可以直接从 Git 仓库 clone:
bash
git clone https://github.com/apache/flink.git
如果你只是学习最新开发分支,可以直接 clone。
如果你是针对 Flink 2.2.0 做研究,建议切到对应版本分支或 tag,避免源码版本与文档描述不一致。
2. Maven
Flink 2.2.0 要求:
- Maven 3.8.6
注意,这不是"差不多就行"的地方。
大型 Maven 工程里,版本不合适时,可能不会立刻给你一个很明确的报错,而是各种诡异行为。
所以,比较稳妥的做法是先检查一下版本:
bash
mvn -version
确保输出中 Maven 版本是 3.8.6。
3. JDK
Flink 2.2.0 源码编译要求:
- Java 11
这一点很重要。
不是"JDK 8 也许能试试",也不是"JDK 17 看起来更高版本应该也行"。
官方明确要求构建使用 Java 11,所以最稳妥的方案就是直接使用 JDK 11。
同样建议先检查:
bash
java -version
javac -version
确保当前 shell 环境里生效的版本确实是 Java 11。
三、最基础的 Flink 编译命令
如果你只想先把 Flink 编译成功,最简单的方法就是在源码根目录执行:
bash
mvn clean install -DskipTests
这个命令可以拆开理解:
1. clean
先清理历史构建产物。
也就是把之前编译出来的内容先删掉,避免旧文件干扰当前构建。
2. install
执行完整构建,并把产物安装到本地 Maven 仓库。
这样后续本地模块之间的依赖也能正常解析。
3. -DskipTests
跳过测试。
对于第一次构建,或者你只是想尽快把工程编出来,这个参数非常实用。
因为 Flink 的测试量不少,如果全量跑测试,整体耗时会明显上升。
所以,这条命令其实就是在做一件事:
先别管测试,优先把整个 Flink 工程干净地编译出来。
四、为什么第一次建议先用这条最朴素的命令?
很多人一上来就想加各种优化参数,比如:
- 并行编译
- 跳过 WebUI
- fast profile
- 只编某些模块
这些都没错,但我还是建议第一次先用:
bash
mvn clean install -DskipTests
原因很简单:
先确认"能不能正常编",再考虑"怎么编得更快"。
如果你一开始就把参数加满,后面一旦失败,你会很难判断问题到底出在:
- 环境没配对
- Maven 版本不对
- JDK 不对
- 某个 profile 冲突
- 某个优化参数带来的副作用
所以第一轮构建,建议尽量朴素。
只要这一步过了,后面再追求编译提速就会更踏实。
五、如何让 Flink 编译更快?
等你确认基础构建没问题之后,就可以开始考虑"速度"了。
因为 Flink 工程体量不小,完整构建时间并不短。
尤其在机械硬盘、普通 SSD、内存较小或者 CPU 核心较少的机器上,等待会更加明显。
官方给了几种典型加速方式。
1. 跳过测试
这一点前面已经提到过了:
bash
-DskipTests
这通常是最直接的提速方式。
2. 使用 fast Maven Profile
可以通过:
bash
-Dfast
跳过一些 QA 插件和 javadoc 生成。
这对构建速度影响非常明显,尤其是本地开发场景。
因为你本地频繁编译时,通常并不需要每次都完整跑 QA 和文档生成。
3. 跳过 WebUI 构建
Flink 提供了一个专门的 profile:
bash
-Pskip-webui-build
这个参数用于跳过 WebUI 编译。
为什么这个参数很有用?
因为 WebUI 构建往往会带来额外文件读写,而这些读写在慢存储设备上尤其耗时。
如果你当前根本不关心 Web UI,只是做 runtime 或 API 层开发,那完全可以跳过。
4. 使用 Maven 并行构建
例如:
bash
-T 1C
表示按 CPU 核心数并行构建模块。
如果你的机器核心数较多,这个参数能明显提高速度。
例如一个推荐的加速命令是:
bash
mvn clean install -DskipTests -Dfast -Pskip-webui-build -T 1C
这条命令的思路很明确:
- 清理旧构建
- 执行安装
- 跳过测试
- 跳过额外 QA / javadoc
- 跳过 WebUI
- 尽量并行利用 CPU
对于日常本地开发,这已经是一个很实用的高频命令组合。
六、并行构建虽然快,但为什么官方提醒要小心?
这里有一个特别值得注意的点。
虽然 Maven 并行构建能提速,但官方也明确提醒:
并行构建可能因为 maven-shade-plugin 的问题出现死锁。
也就是说,-T 1C 虽然好用,但它不是永远安全的。
1. 为什么会这样?
因为 Flink 是大型多模块项目,shade、打包、聚合这些过程本身就比较复杂。
在并行场景下,某些插件组合可能会触发死锁或不稳定行为。
2. 官方更推荐的做法
更稳妥的思路是分两步走:
第一步:前置阶段并行执行
例如:
validatetest-compiletest
这些前置阶段可以考虑并行跑。
第二步:最终打包阶段单线程执行
例如:
packageverifyinstall
这些最终产物阶段尽量单线程,减少插件死锁概率。
也就是说,官方态度并不是"不让你并行",而是提醒你:
并行构建适合加速前置阶段,但最终打包阶段最好保守一点。
如果你只是日常本地开发,偶尔用并行构建通常问题不大;
但如果你追求稳定、尤其在 CI 或关键构建里,还是要更谨慎。
七、fast 和 skip-webui-build 为什么能明显提升速度?
这一点官方也特别强调了。
fast 和 skip-webui-build 对构建时间影响很大,尤其在 较慢的存储设备 上更明显。
原因在于它们会减少大量:
- 小文件读写
- QA 相关处理
- javadoc 生成
- WebUI 构建相关中间文件操作
而小文件 IO,恰恰是很多存储设备最容易拖慢整体速度的地方。
所以你会发现一个很有意思的现象:
- CPU 并不是一直满载
- Maven 也不是一直卡死
- 但整体构建时间就是长
这背后往往不是"算不动",而是"文件系统折腾太多"。
因此在本地开发阶段,-Dfast -Pskip-webui-build 往往是非常划算的优化。
八、如果你还要构建 PyFlink,事情会稍微复杂一点
主工程编译完成之后,如果你还需要构建可供 pip 安装的 PyFlink 包,就还得继续准备 Python 相关环境。
这部分很多 Java 开发者第一次会有点陌生,但其实不算特别难。
九、构建 PyFlink 之前要准备什么?
PyFlink 构建的前提条件主要有两类。
1. 先把 Flink 主工程编好
这一点非常关键。
如果你想打包 PyFlink,必须先完成 Flink 主工程构建。
也就是说,前面的:
bash
mvn clean install -DskipTests
不是可选项,而是前置条件。
2. 准备 Python 环境
官方要求 Python 版本为以下之一:
- Python 3.9
- Python 3.10
- Python 3.11
- Python 3.12
可以先检查版本:
bash
python --version
如果版本不在这个范围内,后续很可能会遇到兼容性问题。
十、如果想启用 Cython 扩展支持,还需要 C 编译器
PyFlink 支持可选的 Cython 扩展构建。
如果你想构建带 Cython 扩展支持的 PyFlink,那么还需要系统具备 C 编译器。
不同操作系统的准备方式不同。
1. Linux
很多 Linux 发行版本身已经有 GCC。
如果没有,可以手动安装。
例如 Ubuntu / Debian 下:
bash
sudo apt-get install build-essential
2. macOS
macOS 上一般需要安装:
- Xcode Command Line Tools
这样系统才会具备 GCC 相关工具链能力。
3. Windows
原文这部分没有展开写 Windows,但实际开发里,Windows 通常需要额外准备本地 C/C++ 编译工具链。
如果只是普通 PyFlink 打包,不强依赖 Cython 扩展,也可以先不走这条路径。
十一、PyFlink 依赖如何安装?
在准备构建 PyFlink 之前,还需要安装开发依赖。
可以执行:
bash
python -m pip install --group flink-python/pyproject.toml:dev
这条命令的作用可以理解为:
根据 Flink 源码中 flink-python 的开发依赖声明,安装构建和开发所需依赖。
对于 PyFlink 打包来说,这一步很重要。
如果依赖没装齐,后面 setup.py 构建阶段很容易直接报错。
十二、如何正式打包 PyFlink?
准备工作完成后,进入 Flink 源码根目录,执行官方给出的这一组命令:
bash
cd flink-python
python setup.py sdist bdist_wheel
cd apache-flink-libraries
python setup.py sdist
cd ..
这组命令本质上是在做两件事。
1. 打包 apache-flink
会生成:
- sdist 包
- wheel 包
也就是说,既会生成源码分发包,也会生成 wheel 二进制安装包。
2. 打包 apache-flink-libraries
这里会生成 sdist 包。
十三、PyFlink 打包产物在哪里?
构建完成后,产物位置如下。
1. apache-flink-libraries
会出现在:
text
./flink-python/apache-flink-libraries/dist/
安装方式例如:
bash
python -m pip install apache-flink-libraries/dist/*.tar.gz
2. apache-flink
会出现在:
text
./flink-python/dist/
可以安装 wheel 包,例如:
bash
python -m pip install dist/*.whl
或者使用 sdist 包安装。
对于大多数本地测试和开发场景,直接安装 .whl 往往更方便。
十四、Scala 版本问题:Java 用户可以忽略,但 Scala 用户必须关心
这一节很多纯 Java 用户看到会自动略过,这完全没问题。
因为如果你只使用 Java API 和 Java 相关库,Scala 版本一般不是你最关心的事情。
但如果你涉及:
- Scala API
- Scala 编写的 Flink 模块
- 依赖 Scala 二进制兼容性的项目
那么这一节就必须认真看。
1. 为什么 Scala 版本要匹配?
因为 Scala 和 Java 不一样。
Java 很强调向后兼容,而 Scala 的二进制兼容性并没有那么"宽松"。
所以,如果你的项目依赖某个特定 Scala 二进制版本,而 Flink 默认构建出来的版本不同,就可能带来依赖冲突或者运行问题。
2. Flink 默认使用哪个 Scala 版本?
从 Flink 1.15 开始:
- 不再支持 Scala 2.11
- 默认使用 Scala 2.12 构建
这意味着大多数场景下,你看到的默认 Flink 构建结果都和 Scala 2.12 绑定。
3. 如果想指定 Scala 版本怎么做?
可以使用:
bash
mvn clean install -DskipTests -Dscala.version=<scala version>
例如你有特殊依赖场景,需要明确控制 Scala 版本,就可以通过这个参数切换。
不过这里也要提醒一点:
不是所有随便写一个 Scala 版本号都能工作。
要确保所选版本确实是 Flink 当前版本支持的二进制 Scala 版本。
十五、加密文件系统下的一个隐藏坑:File name too long
这一节很容易被忽略,但真碰上了会让人非常迷惑。
如果你的 home 目录位于某些加密文件系统中,比如某些 Linux 环境下使用的 encfs,那么在构建 Flink 时,可能遇到:
text
java.io.IOException: File name too long
很多人第一次看到这个错误时,会以为:
- 是路径写错了
- 是 Maven 缓存坏了
- 是插件生成文件异常
其实未必。
有时候问题根源就是:加密文件系统对文件名长度有限制。
而 Flink 构建过程里的某些生成类名、编译产物路径,可能正好超过这个限制。
1. 解决思路
官方给出的 workaround 是,在出问题模块的 pom.xml 中,给相关编译器配置添加:
xml
<args>
<arg>-Xmax-classfile-name</arg>
<arg>128</arg>
</args>
例如如果问题出在 flink-yarn 模块,那么就把这段配置加到该模块 pom.xml 中 scala-maven-plugin 的 <configuration> 里。
2. 这个问题说明了什么?
这其实也从侧面说明 Flink 工程编译不是"小打小闹"的项目。
模块多、插件多、生成过程复杂,底层文件系统特性都可能影响构建结果。
所以如果你在一些特殊环境下编译失败,不要只盯着 Java 和 Maven,也要考虑是不是操作系统层面的限制。
十六、一套更实用的 Flink 本地编译命令建议
如果你问我,日常开发时最常用哪几类命令,我会比较推荐下面几种分层用法。
1. 第一轮验证环境是否正常
bash
mvn clean install -DskipTests
用途:
- 验证 Maven / JDK / 工程依赖是否都正常
- 获取一个最标准的初始构建结果
2. 日常本地提速构建
bash
mvn clean install -DskipTests -Dfast -Pskip-webui-build
用途:
- 提高日常开发效率
- 跳过不必要的额外开销
- 适合本地频繁修改后快速验证
3. 想尝试更快时加并行
bash
mvn clean install -DskipTests -Dfast -Pskip-webui-build -T 1C
用途:
- 进一步榨取本机 CPU 并行能力
- 适合机器配置较高时使用
注意:
- 有概率遇到
maven-shade-plugin相关死锁 - 关键构建时不要完全迷信并行参数
4. 构建 PyFlink
先完成主工程编译,再执行:
bash
cd flink-python
python setup.py sdist bdist_wheel
cd apache-flink-libraries
python setup.py sdist
cd ..
十七、编译 Flink 时,最容易犯的几个错误
这一节很适合拿来做"自查清单"。
1. JDK 版本不对
这是最常见问题之一。
Flink 2.2.0 要求 Java 11 构建,不要想当然用别的版本直接顶上。
2. Maven 版本不匹配
大型项目对 Maven 版本是比较敏感的,别忽视这一点。
3. 一上来就全开并行参数
这样虽然可能快,但一旦失败,定位会很麻烦。
建议先用最基础命令确认环境无误。
4. 主工程没编完就急着打 PyFlink
PyFlink 依赖主工程结果,顺序别搞反。
5. Python 版本超出支持范围
PyFlink 当前支持的是 3.9 到 3.12,太老或太新的版本都可能有问题。
6. 忽略系统层面的限制
例如加密文件系统、路径太长、权限问题,这些都可能成为构建失败的根源。
十八、总结:源码编译不是终点,而是深入 Flink 的起点
回头看这篇文章,你会发现 Flink 源码编译这件事,本质上并不复杂,核心就是几件事:
- 准备正确的源码
- 使用 Maven 3.8.6
- 使用 Java 11
- 先跑通基础构建
- 再根据需要优化构建速度
- 构建 PyFlink 时准备 Python 与可选 C 编译环境
- 特殊场景下关注 Scala 版本与文件系统限制
但另一方面,它又绝不只是"敲一句 Maven 命令"这么简单。
因为当你真的开始自己编译 Flink 源码时,说明你已经不满足于"会用 Flink"了。
你正在往更深一层走:
- 想理解它怎么工作
- 想调试它的内部机制
- 想定制它
- 想贡献它
- 想把它变成真正可控的工程能力
而源码编译,正是这一切的起点。