折腾过Flink新版本的人应该都有过这种体验:下载flink-2.2.1解压后,进到bin目录,才发现以前那些熟悉的bat启动文件全没了,剩下的清一色是.sh结尾的Shell脚本。第一次遇见,整个人是懵的:Windows上到底该怎么启动Flink、怎么提交作业?我当初也是在这个地方卡了很久,翻了好几天资料,写这篇文章就是想把这个坑彻底讲清楚。
这篇文章会围绕新版Flink在Windows环境下的启动问题,讲清楚官方为什么砍掉bat、没有bat之后有哪些可行方案,再给出一套可以直接抄的Docker实操流程和排查经验。适合在Windows本机做Flink开发调试、想升级到2.x版本却不知道怎么下手的朋友,也适合被SQL Client、SQL Gateway这些新概念绕晕的初学者。
1. 新版Flink为什么没有bat启动文件了
1.1 版本演进里,bin目录到底少了什么
先回忆一下老版本。Flink 1.x时代,解压后的bin目录是很热闹的,常见的bat脚本有flink.bat、start-cluster.bat、stop-cluster.bat、sql-client.bat,还有yarn-session.bat、kubernetes-session相关的一些Windows脚本。当时在Windows上调试非常省事,双击start-cluster.bat,本地Standalone集群就起来了,再双击sql-client.bat,就能进去写Flink SQL。
到了Flink 2.0,目录结构就开始变了。大量老旧脚本被清理,官方把重点放到了容器化部署和新的交互方式上。到2.1、2.2这个阶段,bin目录基本就只剩下一堆Shell脚本,比如start-cluster.sh、flink、sql-client.sh、kubernetes-session.sh,bat文件几乎绝迹。很多从1.x升上来的老用户第一次打开新版本压缩包,都会有一种强烈的不适应感:以前那些东西去哪了?
我整理了一个粗略对比,方便你感受变化:
| 能力 | 老版本1.x | 新版本2.2.x |
|---|---|---|
| 启动本地集群 | start-cluster.bat | start-cluster.sh(需WSL/Git Bash/Docker) |
| 提交作业 | flink.bat run | bin/flink run(同样依赖Linux环境) |
| 交互式SQL | sql-client.bat | sql-client.sh或SQL Gateway |
| 容器化部署 | 支持有限 | 官方主推,Docker镜像完善 |
| Windows原生脚本 | 完整 | 基本移除 |
所以问题的本质不是你的压缩包下错了,也不是解压出了问题,而是官方真的改了策略,把Windows原生支持砍掉了。
1.2 官方砍掉bat的真实原因
很多人会问:为什么官方不能顺手保留一套bat脚本?我实际去翻过Flink社区的讨论和JIRA,总结下来主要有几个原因。
第一是维护成本高。Flink的启动脚本不是简单的“java -jar”,它要处理JVM参数、classpath拼接、插件目录扫描、环境变量判断、配置解析,还有一堆分布式集群层面的逻辑。同一套逻辑要维护Shell和bat两套实现,等于所有改动都要双份,任何一个分支忘记同步就会出现很隐蔽的Bug。对官方团队来说,投入产出比太低了。
第二是使用场景太窄。Flink的生产部署基本都在Linux服务器或者容器里,Windows只是本地开发环境。官方数据表明,真正依赖bat脚本跑作业的用户占比不大,大多数人也就是本地测试,用WSL或者虚拟机完全能覆盖。
第三是新架构方向变了。Flink 2.x开始把SQL Gateway、REST API作为重点,Web UI和远程提交逐渐成为主流交互方式。在这种架构下,本地bat脚本的重要性被进一步削弱,官方自然更愿意把精力放在跨平台的能力上。
说白了,官方是在做一个取舍:放弃Windows原生脚本,换来更清晰的代码库和更统一的部署方式。对于用户来说,这不是“不能用了”,而是要换一种思路去启动和使用Flink。
2. 没有bat之后,Windows上还有哪些启动方案
2.1 方案一:WSL里跑官方Shell脚本
既然官方保留的是Shell脚本,那最直接的思路就是在Windows上搞出一个Linux环境,把新版Flink当成标准Linux来跑。WSL是我个人最推荐的本地调试路径,因为它的行为最接近生产环境,以后迁移到服务器上几乎不用改任何东西。
安装WSL很简单,管理员权限运行PowerShell,执行wsl --install -d Ubuntu-22.04,重启后按提示创建用户即可。注意WSL默认是Windows 11系统自带支持的,如果是Windows 10,可能需要在“启用或关闭Windows功能”里勾选“适用于Linux的Windows子系统”。
进入WSL后,先装JDK,Flink 2.x要求Java 11或17,建议直接上17。然后用命令下载Flink二进制包并解压:
sudo apt update && sudo apt install openjdk-17-jdk -y cd ~ wget https://archive.apache.org/dist/flink/flink-2.2.1/flink-2.2.1-bin-scala_2.12.tgz tar -zxvf flink-2.2.1-bin-scala_2.12.tgz cd flink-2.2.1 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 export PATH=$PATH:$JAVA_HOME/bin ./bin/start-cluster.sh启动后直接在Windows浏览器里访问http://localhost:8081,因为WSL2的网络和Windows是互通的,这个地址可以直接打开Flink Web UI。如果要提交作业,再开一个WSL窗口,执行./bin/flink run相关的命令就行。
这个方案有个小坑:如果你是在Windows解压的压缩包,再把文件复制到WSL里,很容易因为权限和换行符问题导致脚本执行异常。我在实际操作中都是把tgz包放到WSL的home目录里重新解压,这样脚本的permission和换行都是干净的。另外WSL里默认内存可能比较小,如果Flink启动后很快就OOM,建议在WSL的配置文件中给足内存,比如.wslconfig里设置memory=8GB。
2.2 方案二:Git Bash临时顶上
如果不想装WSL,还有一个相对轻的替代路径:用Git Bash。Git Bash自带的MinGW环境能解析大部分Shell脚本,新版Flink的start-cluster.sh在Git Bash里确实能跑起来,我试过不止一次。
操作步骤也不复杂。首先确保本机已经装了JDK,然后设置JAVA_HOME,注意路径要转成Git Bash认识的格式,比如Windows路径C:\Program Files\Java\jdk-17要写成/c/Program Files/Java/jdk-17。
export JAVA_HOME="/c/Program Files/Java/jdk-17" export PATH="$JAVA_HOME/bin:$PATH" cd /e/soft/flink-2.2.1 bash bin/start-cluster.sh这里特别提醒一下:不要直接双击.sh文件,也别用Windows自带的cmd去调用,Git Bash有自己的路径转换规则,直接传路径很容易出幺蛾子。另外如果启动时报/bin/bash^M: bad interpreter这类错误,是因为文件换行符是CRLF,用以下命令转换:
sed -i 's/\r$//' bin/start-cluster.sh bin/flink bin/config.sh再执行就能过。
Git Bash方案的好处是省去了装WSL的成本,坏处是兼容性非常脆弱。新版Flink的脚本里有些逻辑在Git Bash下表现不太正常,比如进程管理、信号处理,偶尔会出现集群启动成功但停止脚本失效的情况。我个人的判断是:临时应急可以,长期开发不如老老实实用WSL或者Docker。
2.3 方案三:Docker容器化,最推荐的路线
如果说WSL是最接近原生的方案,那Docker就是最符合Flink官方技术方向的方案。新版Flink官方镜像已经非常完善,JobManager、TaskManager、SQL Client、SQL Gateway都有对应的启动方式,而且镜像里的运行环境是标准Linux,不存在Windows脚本缺失的问题。
Docker方案还有一个额外的好处:它能把Flink运行环境和你的Windows系统彻底隔离。之前我在Windows本机裸装Flink,经常被各种环境变量、JDK版本冲突搞到头大,换成容器之后,本机只需要装一个Docker Desktop,其他的全部丢给容器解决,本地环境干净很多。
如果你已经装了Docker Desktop,用下面的命令拉镜像、起容器:
docker pull flink:2.2.1 docker run -d --name flink-jobmanager --network flink-net \ -p 8081:8081 \ -e FLINK_PROPERTIES="jobmanager.rpc.address: jobmanager" \ flink:2.2.1 jobmanager docker run -d --name flink-taskmanager --network flink-net \ -e FLINK_PROPERTIES="jobmanager.rpc.address: jobmanager taskmanager.numberOfTaskSlots: 4" \ flink:2.2.1 taskmanager注意第一条命令会创建一个名为flink-net的虚拟网络,JobManager和TaskManager必须放在同一个网络里才能互相发现。启动后访问http://localhost:8081就能看到Flink Web UI。这个方案后续扩展起来也方便,比如加一个Flink CDC任务,或者接上SQL Gateway,都只需要在Docker Compse里加服务,不用碰任何bat文件。
3. 实操:用Docker把Flink集群和SQL Gateway跑起来
3.1 为什么现在都在提SQL Gateway
老用户应该还记得Flink SQL Client,之前我们在命令行里敲sql-client.sh进去,然后写SQL、按Ctrl+D提交,整个过程是本地交互式的。SQL Gateway是Flink 1.19引入、2.x逐步强化的新组件,它的核心思路是把SQL执行能力变成一种远程服务。你不需要在本地装完整Flink环境,只要有一个客户端能发HTTP请求,就能创建会话、提交SQL、获取结果。
SQL Gateway之所以重要,是因为它解决了一个很实际的痛点:多人协作和多环境隔离。以前每个人都在自己本地跑SQL Client,依赖各不相同,而SQL Gateway把执行环境统一收敛到服务端,你只需要告诉它“帮我执行这条SQL”,它负责调度、管理会话、返回结果。配合REST API,还能接入前端页面、自动化平台,这比在Windows上双击一个bat文件要先进太多。
3.2 docker-compose配置拆解
我这里直接给一个能用的docker-compose.yml,包含了JobManager、TaskManager、SQL Gateway三个服务。你把这个文件放到一个空目录里,比如E:\flink-docker,然后在这个目录下执行docker compose up -d就行。
version: '3' services: jobmanager: image: flink:2.2.1 ports: - "8081:8081" environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager command: jobmanager taskmanager: image: flink:2.2.1 depends_on: - jobmanager environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager taskmanager.numberOfTaskSlots: 4 command: taskmanager sql-gateway: image: flink:2.2.1 depends_on: - jobmanager ports: - "8083:8083" environment: - | FLINK_PROPERTIES= jobmanager.rpc.address: jobmanager sql-gateway.endpoint.rest.address: 0.0.0.0 sql-gateway.endpoint.rest.port: 8083 command: sql-gateway重点说几个参数的含义。jobmanager.rpc.address必须填jobmanager,这是Docker服务名,Compose会把它解析成JobManager容器的IP地址。taskmanager.numberOfTaskSlots设置了每个TaskManager的Slot数量,我这里设成4,意思是并发执行的任务上限是4个。sql-gateway.endpoint.rest.address设为0.0.0.0,表示监听所有网卡,这样才能被宿主机访问。
端口方面,8081是Flink Web UI和REST API,8083是SQL Gateway的默认REST端口。8081可以随便改,但8083要和SQL Gateway的配置保持一致,不然客户端连不上。这些配置如果你不熟悉,最好先原样跑通,再根据自己的需求调整。
3.3 启动集群并验证
在docker-compose.yml所在目录执行:
docker compose up -d这个命令会启动三个容器。然后执行docker ps查看状态,正常情况下会看到三个容器都在Up状态。如果某个容器一直重启,用docker logs <容器名>查看日志,比如docker logs flink-docker-sql-gateway-1。
打开浏览器访问http://localhost:8081,如果能看到Flink Web UI,说明集群已经起来了。在Web UI的Task Managers标签页里,应该能看到一个TaskManager已经注册进来,Slot显示4个。
如果Web UI里TaskManager一直不出现,优先检查jobmanager.rpc.address配置。最常见的问题是有人把地址写成了localhost,在容器环境里这是不对的,因为每个容器有自己独立的网络栈,localhost指向的是容器自己,不是JobManager。必须写成服务名jobmanager。
3.4 跑一条SQL验证整条链路
集群跑起来之后,需要验证SQL链路。新版Flink的SQL Client支持连到远程Gateway,不需要在本地装完整环境。在宿主机上执行:
docker run -it --rm --network flink-docker_default \ flink:2.2.1 sql-client.sh gateway --host sql-gateway --port 8083注意--network的名字要替换成你实际的网络名,一般是你docker-compose.yml所在目录名后面加_default。如果不知道网络名,先执行docker network ls看一下。进入SQL Client后,先跑一个最简单的语句:
SELECT 'hello flink';能返回结果说明SQL链路是通的。再创建一个Datagen连接器的表来模拟数据流,这是Flink内置的随机数据生成器,不需要外部系统:
CREATE TABLE orders ( order_id INT, amount DECIMAL(10, 2), ts TIMESTAMP(3) ) WITH ( 'connector' = 'datagen', 'rows-per-second' = '1' ); SELECT order_id, amount, ts FROM orders;正常情况下,你会看到每秒生成一行数据,这是验证整个集群最直观的方式。Datagen连接器在新版Flink的发行包里通常已经包含,不需要额外下载,如果报找不到Connector,检查一下lib目录下有没有flink-table-planner-loader和对应的连接器JAR。
3.5 不用客户端,直接调REST API提交SQL
SQL Gateway的一个重要特性是提供REST API,这意味着可以不依赖任何Flink客户端,只用Linux自带的curl或者Windows PowerShell就能提交SQL,这对自动化平台特别有用。我在这里演示一下核心流程。
首先要创建一个会话,执行下面的命令:
curl -X POST http://localhost:8083/v1/sessions返回值里会有一个sessionId,类似c1f5e0e0-...这样的字符串。拿到它后用OpenSession的Session Handle来运行语句:
curl -X POST http://localhost:8083/v1/sessions/<sessionId>/statements \ -H "Content-Type: application/json" \ -d '{"statement": "SELECT 1"}'这里返回的operationHandle用于查询执行结果。整个流程走下来,你会发现Flink已经变成了一个标准服务,你用任何语言写个HTTP调用都能提交SQL,Windows上的bat文件确实变得可有可无了。
4. 常见问题与排查技巧实录
4.1 脚本报错“bad interpreter”或找不到Java
这个在WSL和Git Bash里都容易出现。bad interpreter基本可以确定是换行符问题,使用sed -i 's/\r$//' bin/start-cluster.sh批量转换即可。找不到Java则要检查JAVA_HOME是否设置正确,在WSL里可以执行which java确认,如果没安装JDK,先装;在Git Bash里,记得路径要写成/c/Program Files/Java/...这种格式,不要用Windows反斜杠。
我遇到过最坑的一种情况是JAVA_HOME指向了JRE而不是JDK,Flink启动时会报缺少编译器相关类。解决办法是确保JAVA_HOME指向JDK的根目录,比如C:\Program Files\Java\jdk-17,而不是JRE目录。还可以用$JAVA_HOME/bin/javac来验证,能输出Java编译器版本就说明路径正确。
4.2 TaskManager一直显示Unreachable
这个问题在容器方案里出现频率最高。现象是Web UI能看到JobManager,但TaskManager列表为空或者一直显示不可达。主要原因通常有两个。
一是网络问题。JobManager和TaskManager没有加入同一个Docker网络,或者jobmanager.rpc.address写错了。我在前面已经强调过,容器之间要互相通信就必须在同一个网络里,且连接地址要用服务名,不能用localhost。
二是内存不足。一些老机器默认Docker内存配额比较低,TaskManager启动到一半就被系统杀掉,表现为容器一直处于Restarting状态。可以在Docker Desktop的Settings里调大内存,比如8GB以上,同时给TaskManager设置合理的JVM参数,比如taskmanager.memory.process.size: 2048m。
4.3 SQL Gateway启动不起来或连不上
启动不起来先看日志,常见的是端口被占用或者配置参数格式错误。8083端口被占用时,在docker-compose.yml里换一个宿主端口即可,比如"8084:8083",客户端连接时端口也要跟着改成8084。
还有一个容易踩的坑:在SQL Client连接Gateway时,--host后面如果填了localhost,在Git Bash或Windows某些网络环境下连不上。这种情况下优先用容器的服务名sql-gateway作为host,或者查一下SQL Gateway容器映射到宿主机的IP,直接填那个IP。
4.4 我在Windows上折腾Flink的避坑清单
最后整理几条我个人摸索出来的经验,这些内容在官方文档里基本找不到,但实操中非常有用。
第一,不要尝试把老版本的bat脚本直接复制到新版本里用。新老版本的目录结构、lib依赖、启动参数差异非常大,旧脚本复制过来几乎必挂,而且报错信息不直观,排查起来非常痛苦。
第二,Windows解压Flink压缩包时,推荐用7-Zip这类工具,右键解压到位。Windows自带的资源管理器解压偶尔会出现权限问题和长路径问题,尤其bin目录下有多个同名的.sh和配置联动文件,换行符也容易被打乱。直接在WSL里解压是最省心的。
第三,本地开发调试尽量用Docker Compose管理多个Flink组件。很多人习惯一个个docker run,结果容器名、网络名记混,最后资源泄漏。用Compose能在一个文件里管理所有服务,重置环境也很方便,一个docker compose down -v全部干净。
第四,SQL Client进入后如果输中文乱码,多半是Windows终端的编码问题。在PowerShell里执行chcp 65001切换成UTF-8代码页,再进SQL Client一般就能解决。
写到这里,新版Flink没有bat启动文件这个问题,应该已经被拆得比较透了。核心思路就一句话:别再想着找bat了,把思维方式从“Windows脚本启动”切换到“Linux环境或容器化部署”。我个人在本地开发时最常用的组合是WSL用来跑全套Flink作业调试,Docker用来验证集群部署和多组件联动,两者搭配下来基本能覆盖所有场景。如果你现在还在纠结没有bat不能用,我建议按文中第二条思路,先把Docker方案跑通,你会发现新版Flink不仅不难用,反而比以前那套bat体系灵活得多。