前阵子我换了 Flink 2.2.x 跑本地实验,解压完安装包之后,习惯性去 bin 目录下找 start-cluster.bat,结果把整个目录翻了个底朝天,只剩下 start-cluster.sh、sql-client.sh、sql-gateway.sh 这些 Linux 脚本,一个 .bat 都没有。Windows 下玩 Flink 的朋友应该都有同感:以前照着教程双击 start-cluster.bat 就能把集群拉起来,到了新版直接失效,网上还有很多资料停在旧路径,跟着敲只会得到“系统找不到指定的文件”。
这个事其实不算 bug,而是官方在新版本里把 Windows 启动脚本整个拿掉了。但它对 Windows 用户的冲击是实打实的:本地开发、写 Flink SQL 实验、用 CDC 同步数据,第一步就卡住,后面全没法玩。这篇博文我会把现象、原因、三种可行解法和后续高频报错一次性讲清楚,覆盖从纯小白到有点基础想搞清楚原理的同学,让你拿到新版 Flink 之后不用再为启动文件发愁。
1. 为什么新版 Flink 不再提供 bat 启动文件
1.1 从旧版到新版,启动脚本到底经历了什么
在 Flink 1.x 比较流行的阶段,发行包的 bin 目录里通常有 start-cluster.bat、stop-cluster.bat、sql-client.bat、flink.bat 这几个 Windows 批处理文件。当时很多教程在 Windows 上跑 Flink,都是从“双击 start-cluster.bat”开始的,本地起一个 JobManager 和 TaskManager,然后访问 8081 端口看 Web UI,整个链路很简单。
但越往后,官方对 Windows 批处理脚本的维护意愿越来越低。我观察过的发行包变化是:早期 bat 文件还偶尔更新,后来慢慢变成“只保证 Linux 脚本可用,bat 能跑就行”,再往后干脆就从新版本发行包里移除了。到了 Flink 2.x 这类新版本,bin 目录里你已经找不到任何 .bat,全是 .sh,这是非常明显的变化。
官方这么做的原因并不难理解。Flink 本质是跑在 JVM 上的分布式计算引擎,跨平台能力来自 Java 本身,但启动脚本是另一回事:Windows 批处理要额外处理路径转换、命令兼容、环境变量差异,维护成本高,又缺乏自动化测试覆盖。加上主流部署场景从物理机转向容器、K8s,Linux 才是绝对主战场,Windows 在官方视角里更多是“开发者的笔记本环境”,优先级自然被一砍再砍。
1.2 没有 bat 文件,Windows 用户到底缺了什么
很多人第一反应是“没有 bat 就不能跑 Flink 了”,其实不对。Flink 的核心是基于 Java 的,只要 JVM 环境正确,Windows 完全可以运行。真正缺的,是一个“一键双击就能启动”的入口。
没有了 start-cluster.bat,意味着你要自己处理 classpath、配置目录、日志路径、进程管理这些事。对刚接触 Flink 的新手来说,这个门槛立刻就被抬高了。对下面的几类人影响尤其明显:
- 想用 Flink SQL 做本地数据分析、练习窗口和 watermark 语法的同学;
- 在 Windows 台式机上做 Flink CDC 同步实验的开发者;
- 公司规定开发环境必须是 Windows,但又想快速验证 Flink 任务的工程师;
- 看旧教程学习,结果被“找不到批处理文件”卡住的学生。
另外还有一个连带问题:旧版里 flink.bat 承担了提交作业、查看任务列表等功能,现在这个入口也没了。所以严格来说,缺的不是一个文件,而是一整套面向 Windows 用户的操作入口。
1.3 整体解法思路:让官方 shell 脚本在 Windows 上“复活”
面对没有 bat 的状况,解决办法其实可以分成三条路线:
- 给 Windows 装一个 bash 环境,直接运行官方 .sh 脚本。最典型的是 Git Bash,或者完整的 WSL。
- 不装任何模拟环境,用 CMD 或 PowerShell 手工调用 java 命令,把 JobManager 和 TaskManager 拉起来。
- 完全抛弃本地脚本,用 Docker 容器跑 Flink,让镜像里的 Linux 环境来接管启动动作。
这三条路线各有适用场景:本地做实验、只想尽快把 Flink SQL 跑起来,我用得最多的是 Git Bash;如果追求“和服务器一致”的体验,WSL 或者 Docker 更合适;纯手工 CMD 其实最不推荐,但它能帮你理解 Flink 启动的底层逻辑。后面几章我会按优先级逐个展开。
2. 首选方案:用 Git Bash 把官方 .sh 脚本重新“捡起来”
2.1 为什么我推荐 Git Bash 而不是直接上 WSL
Git Bash 是随 Git for Windows 一起安装的轻量模拟环境,启动快、占用小,自带 bash、sed、awk、cygpath 等工具,Flink 官方 .sh 脚本里依赖的 dirname、readlink、变量处理这些能力它基本都能满足。
相比之下,WSL 是一个完整的 Linux 子系统,功能更强大,但也更重。如果你只是为了启动 Flink 集群,装一个 WSL 再配发行版,有点杀鸡用牛刀。而且 WSL 访问 Windows 盘符下的文件时,路径挂载、文件权限、换行符都可能踩坑,对新手不友好。
Git Bash 还有一个隐藏优势:它和 Git 绑定,很多开发者电脑上本来就装着。就算没装,安装过程也就是一路 Next,不需要额外配虚拟化,不需要重启系统,学习成本非常低。所以我个人给 Windows 本地开发场景的默认建议就是:用 Git Bash 跑 .sh。
2.2 启动 standalone 集群的完整流程
我用 Git Bash 跑 Flink 新版本的流程基本是固定的,按下面几步操作就能把集群拉起来:
- 安装 Git for Windows,安装选项保持默认即可,右键菜单会出现“Git Bash Here”。
- 下载新版 Flink 二进制包,解压到一个没有中文、没有空格的路径,比如 D:\env\flink-2.2.1。这一步很重要,路径里有空格或中文,后面启动脚本很可能报奇怪错误。
- 确认 JDK 环境。新版 Flink 对 JDK 版本有要求,一般 JDK 8 或 11 都算稳妥,具体以官方文档为准。配置好 JAVA_HOME,并在 CMD 里确认 java -version 能正常输出。
- 进入 Flink 根目录,在空白处右键选择“Git Bash Here”,然后执行:
./bin/start-cluster.sh - 看到 “Starting cluster.” 输出后,打开浏览器访问 http://localhost:8081 ,能看到 Flink Web UI 就说明 JobManager 起来了。
- 用完以后,在同一个 Git Bash 窗口执行:
./bin/stop-cluster.sh
如果你执行 start-cluster.sh 后没有任何反应,或者访问不到 8081,第一步不是怀疑脚本坏了,而是去看 log 目录下的日志文件。Flink 会把 JobManager、TaskManager 的启动日志都写到 log 目录,真正的报错信息都在里面。
2.3 用同样方式启动 SQL Client 和 SQL Gateway
很多人在 Windows 上玩 Flink 不只是为了看 Web UI,更关心 Flink SQL 能不能跑。新版发行包里的 SQL Client 入口同样只剩 .sh,启动方式:
./bin/sql-client.sh embedded这样会进入一个交互式 SQL 环境,可以直接执行 CREATE TABLE、SELECT 等语句。如果你想跑一个写好的 SQL 文件,可以用:
./bin/sql-client.sh -f /path/to/query.sql如果想把 SQL 能力暴露成 REST API,方便其他程序调用,需要启动 SQL Gateway。新版 Flink 有独立的 sql-gateway.sh:
./bin/sql-gateway.sh start -Dsql-gateway.endpoint.rest.host=localhost启动后用 curl 或者浏览器访问对应的 HTTP 端口,可以看到 SQL Gateway 的 REST 接口。不同小版本可能默认端口不同,启动日志里会打印实际监听地址,以日志为准。
这里要注意一个常见误区:SQL Client 本身只是一个命令行客户端,真正执行 SQL 还是要靠 Flink 集群。所以你必须先把 start-cluster.sh 启动起来,再开第二个 Git Bash 窗口去跑 sql-client.sh,否则连接不到集群。
2.4 做一个一键启动辅助“bat”文件
虽然 Flink 官方不给 bat 了,但我们可以自己写一个简单的批处理,间接调用 Git Bash 里的 bash.exe,实现“双击就启动集群”的效果。
下面这个脚本的思路是:先在 CMD 里把工作目录切到 Flink 根目录,然后用 Git Bash 执行官方脚本。这么做可以避开 Windows 路径传给 bash 时常见的盘符转换问题。
@echo off chcp 65001 >nul set FLINK_HOME=D:\env\flink-2.2.1 cd /d "%FLINK_HOME%" echo Starting Flink cluster... start "Flink JobManager" "%ProgramFiles%\Git\bin\bash.exe" -lc "./bin/start-cluster.sh" timeout /t 3 >nul start "" http://localhost:8081 echo Flink cluster has been started. Press any key to stop. pause >nul echo Stopping Flink cluster... "%ProgramFiles%\Git\bin\bash.exe" -lc "./bin/stop-cluster.sh" pause脚本里的 chcp 65001 是为了让 CMD 窗口显示 UTF-8 输出不乱码。start 命令会让 Git Bash 在新窗口运行,所以原来的 CMD 窗口还能继续等用户按任意键停止集群,算是一个很简单的 start/stop 一体工具。
如果你把 Flink 装到了其他路径,记得同步修改 FLINK_HOME。这个脚本和官方 bat 的核心差异只是多了一层对 Git Bash 的调用,但已经足够解决“没有启动文件”的问题了。
3. 不想装 Git Bash?用 CMD/PowerShell 手工拉起来
3.1 手工启动的底层逻辑
如果你因为某些原因不想装 Git Bash,或者你只是想知道 Flink 的启动脚本到底做了什么,那这一章能帮上忙。
start-cluster.sh 表面上一行命令搞定集群,实际操作可以拆成两部分:启动一个 JobManager 进程,再启动一个或多个 TaskManager 进程。JobManager 在 standalone 模式下的入口类是 org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint,TaskManager 的入口类是 org.apache.flink.runtime.taskexecutor.TaskManagerRunner。
知道了这两个类名,理论上你就可以用 java -cp 手动拉起整个集群。难点在于 classpath 要包含 lib 目录下的所有依赖 jar,还要设置日志、配置目录等系统参数,命令会非常长。
3.2 手工启动 JobManager 与 TaskManager 的命令骨架
如果你想体验一把手工启动,可以用 PowerShell 写一个简单版本。下面是一个示意性的骨架,帮助你理解过程,实际使用时要根据你的目录和版本调整:
$env:FLINK_HOME = "D:\env\flink-2.2.1" $env:FLINK_CONF_DIR = "$env:FLINK_HOME\conf" $env:FLINK_LIB_DIR = "$env:FLINK_HOME\lib" $env:FLINK_PLUGINS_DIR = "$env:FLINK_HOME\plugins" $jobManagerClass = "org.apache.flink.runtime.entrypoint.StandaloneSessionClusterEntrypoint" $taskManagerClass = "org.apache.flink.runtime.taskexecutor.TaskManagerRunner" Start-Process java -ArgumentList @( "-cp", "$env:FLINK_HOME\lib\*", $jobManagerClass, "-Dlog.file=$env:FLINK_HOME\log\jobmanager.log" ) Start-Process java -ArgumentList @( "-cp", "$env:FLINK_HOME\lib\*", $taskManagerClass, "-Dlog.file=$env:FLINK_HOME\log\taskmanager.log" )说实话,这个方案在日常使用中并不舒服。日志路径、配置项、内存参数稍微写错一点,进程起不来或起了一半失败,排查起来比用官方脚本麻烦很多。所以我更建议把这一章当成“理解原理”的辅助材料,而不是长期使用的启动方式。
3.3 批处理和 PowerShell 里最容易踩的坑
如果你就是要走手工启动这条路,下面几个坑提前帮你排掉:
- JDK 版本问题。Flink 2.x 对 JDK 版本有最低要求,如果本机装的是过老的 JDK,会直接报 UnsupportedClassVersionError。先确认 java -version 输出。
- 路径里有空格。手工写 classpath 时,路径必须加引号,否则 JVM 会认为空格后面是新增参数,直接启动失败。
- CMD 通配符和 bash 不同。CMD 里 -cp "D:\lib*" 的引号位置很敏感,建议直接用 PowerShell 的数组参数方式,避免被 CMD 解析器拆坏。
- 环境变量不生效。新装的 JAVA_HOME 或 FLINK_HOME 不会立刻在当前窗口生效,要重新打开一个 CMD 或 PowerShell 窗口。
- 日志参数缺失。手工启动时如果不指定 -Dlog.file,日志可能打到控制台或者根本找不到,出了问题没法定位。
3.4 内存参数为什么不能随便乱调
手工启动时很多人会顺手加 -Xmx/-Xms,但这里有个隐藏问题:Flink 从 1.11 开始引入了一套内存模型,jobmanager.memory.process.size 和 taskmanager.memory.process.size 才是真正决定老年代、堆内存、堆外内存的参数,而且会覆盖 JVM 层面的部分设置。
如果你只是手工启动,没改 flink-conf.yaml,那么你就算在命令行里写了 -Xmx4g,也可能发现实际堆内存不是 4g,或者进程因为配置不一致直接失败。正确的做法是先修改 conf/flink-conf.yaml 里的内存配置,再启动进程。
这也是为什么我不太推荐纯手工启动的原因之一:可配置项太多,官方脚本已经把配置和命令组织好了,省略这些细节,埋下的坑反而更多。
4. 想省事就上容器:WSL 与 Docker
4.1 WSL 下直接复用 Linux 脚本
如果你用的 Windows 10/11 开了 WSL,那情况会简单很多。在 WSL 里,Flink 脚本和你在 Linux 服务器上完全一致,没有任何“Windows 没有 bat”的概念。
操作上最常见的坑有两个。第一是文件权限,从 Windows 下载解压的文件可能在 WSL 里没有可执行权限,需要先:
chmod +x bin/*.sh第二是换行符。如果你用 Windows 自带的记事本或某些编辑器改过脚本,文件可能是 CRLF 换行,bash 执行时会报$'\r': command not found这种错误。解决办法是把换行符转成 LF:
sed -i 's/\r$//' bin/*.sh处理完这两步,按照正常 Linux 方式执行 ./bin/start-cluster.sh 即可。WSL 的好处是环境干净,和服务器行为一致,适合需要长期稳定跑 Flink 实验的同学。
4.2 Docker Compose 部署 Flink 和 Flink CDC
如果你连脚本都不想碰,直接用 Docker 是最省事的方式。官方 Flink 镜像本身就内置了完整的 Linux 环境,启动动作全部在容器里完成,Windows 本地只需要一个 Docker Desktop。
下面是一个最精简的 Docker Compose 示例,拉起一个 JobManager 和一个 TaskManager:
services: jobmanager: image: flink:2.2.1 ports: - "8081:8081" command: jobmanager environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager taskmanager: image: flink:2.2.1 depends_on: - jobmanager command: taskmanager environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager taskmanager.numberOfTaskSlots: 2在项目目录下执行 docker compose up -d,然后访问 http://localhost:8081 就能看到集群界面。
对于 Flink CDC 3.x 这类新组件,Docker 部署优势更明显。你可以把 CDC 需要的 connector jar 挂载进容器,或者直接使用 flink-cdc 相关的 compose 编排,避免在 Windows 本地折腾 jar 依赖和启动文件匹配。版本匹配上要特别注意:Flink 2.x 对应的 CDC 主版本和 Flink 1.x 不是一个系列,用错版本会出现类找不到或者 Source 无法实例化的问题,去看官方版本匹配表最稳妥。
4.3 三种方式怎么选
我把 Git Bash、纯手工命令、Docker/WSL 三种方式做了个对比,方便你对号入座。
| 方式 | 核心原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| Git Bash 跑 .sh | 在 Windows 上模拟 bash 环境 | 轻量、无需虚拟化、启动快 | 可能出现少量路径和命令兼容问题 | 本地快速实验、学习 Flink SQL |
| 纯 CMD/PowerShell 手工启动 | 直接调用 java 入口类 | 不依赖额外工具 | 命令复杂、易出错、维护成本高 | 理解启动原理、应急排障 |
| Docker / WSL | 在 Linux 环境运行官方脚本 | 行为与服务器一致、干净可控 | 占用资源多,Docker 在 Windows 上依赖虚拟化 | 长期开发、部署预演、CDC 全链路验证 |
我个人的倾向是:只想跑个 SQL 验证想法,用 Git Bash;想认真搞数据同步或者做项目,直接上 Docker。
5. 启动文件解决了,后面这几个问题你大概率也会遇到
5.1 SQL Client 报错:找不到 jdbc factory
当你终于把集群启动起来,兴致勃勃在 SQL Client 里执行 CREATE TABLE 连接 MySQL,结果弹出一句“Could not find any factory for identifier 'jdbc'”,大概率是因为 Flink 默认发行包并没有内置 JDBC connector。
解决思路很简单:把 flink-connector-jdbc 的 jar 包下载下来,放到 Flink 根目录的 lib 文件夹中,然后重启 SQL Client。注意版本要和 Flink 大版本匹配,比如 Flink 2.x 对应连接器 3.x 系列,具体版本号以 Maven Central 或官网连接器页为准。
你也可以用 sql-client.sh 的 -C 参数临时指定 jar 路径,但我实测下来,直接把 jar 放进 lib 是最省心的方式,省得每次启动都要带参数。
5.2 连接 MySQL 时 Driver 冲突
解决了 jdbc factory 问题,你还可能遇到第二个报错:ClassNotFoundException: com.mysql.cj.jdbc.Driver。这是因为 Flink 的 JDBC connector 只负责翻译 SQL 和建立连接框架,真正和 MySQL 通信还需要 MySQL 驱动包。
把 mysql-connector-j 8.x 的 jar 也放到 lib 目录,问题一般就能解决。这里要注意,不要同时放多个版本的 MySQL 驱动,否则可能出现驱动加载错乱、连接行为诡异的故障。我在实际排障中见过一个例子:lib 目录里既有 mysql-connector-java 5.x,又有 8.x,结果 Flink 随机使用错误的驱动,报加密协议不支持的毛病,折腾了整整一下午。
5.3 8081 端口被占用
新版 Flink 启动时如果发现 8081 被占用,Web UI 会起不来,但进程可能已经跑了,看起来像“集群启动失败”。排查方法是在 CMD 里执行:
netstat -ano | findstr 8081看到 PID 之后,打开任务管理器结束对应进程,或者修改 conf/flink-conf.yaml 里的 rest.port 改成别的端口。
这里还有一个隐藏坑:如果你修改了 rest.port,但没注意内部通信端口 jobmanager.rpc.port 和 taskmanager 的数据端口是否冲突,TaskManager 可能注册不上。我遇到过只改了 HTTP 端口、忘了检查内部端口,结果集群界面一直显示 0 个 TaskManager 的情况,日志里全是连接超时。
5.4 SQL 里 WATERMARK 和 CDC 版本不要踩雷
很多人在新版 SQL Client 里用 WATERMARK 语法时会报错,常见原因是把 WATERMARK 写在了 SELECT 外部,或者使用了旧版的写法。新版 Flink 要求在 DDL 里直接定义 watermark 字段,格式要严格符合语法,多看官方文档的示例最靠谱。
Flink CDC 那边也是类似情况。CDC 2.x 和 3.x 对应不同的 Flink 版本,用错版本启动任务时,会出现找不到 SourceFunction 或者 factory 的异常,第一反应不要怀疑代码,先去核对版本匹配表。
我的习惯是用 Docker 部署 Flink CDC,因为镜像内部把版本兼容问题处理得相对干净,本地只要挂载好配置文件即可。
5.5 新手启动排障自检清单
下面这张表是我调试 Flink 启动问题时经常对照的速查表,按症状查原因,能省不少时间。
| 症状 | 可能原因 | 快速定位 |
|---|---|---|
| 执行 start-cluster.sh 无输出,8081 无法访问 | Java 版本不对、JAVA_HOME 未配置 | java -version,检查环境变量 |
| 日志出现 ClassNotFoundException | 依赖 jar 缺失或版本不匹配 | 查看具体是哪个类,找对应 jar 放入 lib |
| 启动后 Web UI 有 JobManager,但没有 TaskManager | 内部端口冲突、taskmanager 未注册 | 看 TaskManager 日志、检查 conf/flink-conf.yaml |
| SQL Client 找不到 jdbc factory | JDBC connector 未安装 | 下载 flink-connector-jdbc 放入 lib |
| 中文目录或空格路径导致启动异常 | 路径解析问题 | 把 Flink 移到纯英文无空格路径 |
| WSL 下脚本报 $'\r' 相关错误 | 换行符是 CRLF | sed -i 's/\r$//' bin/*.sh |
最后再分享一个小技巧:我每次在 Windows 上跑 Flink 实验,都会先单独建一个干净的实验目录,比如 D:\env\flink-2.2.1,所有连接器 jar 都统一放到 lib 目录,升级版本之前先看一眼官方兼容性说明再动手。这样即使新版没有 bat 启动文件,我也能用 Git Bash 在几分钟内把集群和 SQL 环境全部拉起来。希望这套经验能让你少踩几次坑,顺利把 Flink 在 Windows 上跑起来。