flink ./bin/start-cluster.sh 帮我做成开机启动
可以。你是 tar.gz 方式安装 Flink,最简单稳妥的方式就是给 ./bin/start-cluster.sh 做一个 systemd 服务,让 Ubuntu 开机自动执行,同时关机时执行 stop-cluster.sh。
假设你的 Flink 安装目录是:
/app/flink
运行 Flink 的 Linux 用户是:
userroot
如果实际目录不同,把下面 /app/flink 替换成你的实际目录即可。
1. 先确认手动启动没问题
cd /app/flink
./bin/start-cluster.sh
jps
正常应该能看到类似:
StandaloneSessionClusterEntrypoint TaskManagerRunner
然后先停掉:
./bin/stop-cluster.sh
2. 创建 systemd 服务
执行:
sudo vim /etc/systemd/system/flink.service
写入:
[Unit] Description=Apache Flink Standalone Cluster Documentation=https://flink.apache.org/ After=network-online.target Wants=network-online.target [Service] Type=oneshot User=userroot Group=userroot WorkingDirectory=/app/flink Environment="FLINK_HOME=/app/flink" Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" ExecStart=/app/flink/bin/start-cluster.sh ExecStop=/app/flink/bin/stop-cluster.sh RemainAfterExit=yes TimeoutStartSec=120 TimeoutStopSec=120 [Install] WantedBy=multi-user.target
其中最需要确认的是:
User=userroot WorkingDirectory=/app/flink JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
你的 Java 21 路径可以这样确认:
readlink -f $(which java)
例如返回:
/usr/lib/jvm/java-21-openjdk-amd64/bin/java
那么:
Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64"
就是正确的。
3. 让 systemd 读取服务
sudo systemctl daemon-reload
4. 设置开机启动
sudo systemctl enable flink
会看到类似:
Created symlink /etc/systemd/system/multi-user.target.wants/flink.service
5. 现在直接测试启动
sudo systemctl start flink
查看状态:
sudo systemctl status flink
正常应该类似:
● flink.service - Apache Flink Standalone Cluster Loaded: loaded (/etc/systemd/system/flink.service; enabled) Active: active (exited)
这里的:
active (exited)
是正常的,不是 Flink 挂了。
因为:
start-cluster.sh
本身只是负责把 JobManager 和 TaskManager 拉起来,然后脚本就退出了。
我们配置了:
Type=oneshot RemainAfterExit=yes
所以 systemd 会认为 Flink 服务仍然处于运行状态。
再执行:
jps
应该能看到:
StandaloneSessionClusterEntrypoint TaskManagerRunner
还可以访问:
http://服务器IP:8081
确认 Flink Web UI 正常。
6. 测试 systemctl 停止
执行:
sudo systemctl stop flink
然后:
jps
Flink 的:
StandaloneSessionClusterEntrypoint TaskManagerRunner
应该消失。
再重新启动:
sudo systemctl start flink
7. 最后测试真正的开机自启动
检查:
sudo systemctl is-enabled flink
返回:
enabled
然后重启服务器:
sudo reboot
服务器起来以后:
systemctl status flink
以及:
jps
如果看到 JobManager 和 TaskManager,就完成了。
你最终以后只需要记住这几个命令:
# 启动
sudo systemctl start flink
# 停止
sudo systemctl stop flink
# 重启
sudo systemctl restart flink
# 查看状态
sudo systemctl status flink
# 开机启动
sudo systemctl enable flink
# 取消开机启动
sudo systemctl disable flink
对于你现在这种 tar.gz 单机 Standalone Flink 部署,我建议就用 Type=oneshot + RemainAfterExit=yes,不要直接把 start-cluster.sh 配成 Type=simple。 因为 start-cluster.sh 本身启动完 JobManager/TaskManager 后会退出,Type=simple 容易让 systemd 对进程生命周期产生错误判断。
/usr/local/flink 我的安装目录
那就把 Flink 目录统一改成 /usr/local/flink。
创建服务文件:
sudo vim /etc/systemd/system/flink.service
内容改成:
[Unit] Description=Apache Flink Standalone Cluster Documentation=https://flink.apache.org/ After=network-online.target Wants=network-online.target [Service] Type=oneshot User=userroot Group=userroot WorkingDirectory=/usr/local/flink Environment="FLINK_HOME=/usr/local/flink" Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" ExecStart=/usr/local/flink/bin/start-cluster.sh ExecStop=/usr/local/flink/bin/stop-cluster.sh RemainAfterExit=yes TimeoutStartSec=120 TimeoutStopSec=120 [Install] WantedBy=multi-user.target
先确认 Java 路径:
readlink -f $(which java)
如果返回例如:
/usr/lib/jvm/java-21-openjdk-amd64/bin/java
那上面的:
Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64"
就是对的。
然后依次执行:
sudo systemctl daemon-reload
sudo systemctl enable flink
sudo systemctl start flink
sudo systemctl status flink
再确认 Flink 进程:
jps
正常应该有类似:
StandaloneSessionClusterEntrypoint TaskManagerRunner
也可以看 8081 是否起来:
ss -lntp | grep 8081
以后启动/停止就不用再执行:
/usr/local/flink/bin/start-cluster.sh
直接:
sudo systemctl start flink
sudo systemctl stop flink
sudo systemctl restart flink
sudo systemctl status flink
还有一个地方建议你先确认一下:/usr/local/flink 是谁拥有的。
执行:
ls -ld /usr/local/flink
如果显示类似:
drwxr-xr-x ... userroot userroot ... /usr/local/flink
那么:
User=userroot Group=userroot
就可以直接用。
如果显示的是其他用户,把结果发我,我可以直接帮你把 flink.service 调整到完全匹配你这台服务器。
User=wenhaoran Group=wenhaoran
那就把 systemd 服务里的运行用户改成 wenhaoran。
/etc/systemd/system/flink.service 建议最终写成:
[Unit] Description=Apache Flink Standalone Cluster Documentation=https://flink.apache.org/ After=network-online.target Wants=network-online.target [Service] Type=oneshot User=wenhaoran Group=wenhaoran WorkingDirectory=/usr/local/flink Environment="FLINK_HOME=/usr/local/flink" Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64" ExecStart=/usr/local/flink/bin/start-cluster.sh ExecStop=/usr/local/flink/bin/stop-cluster.sh RemainAfterExit=yes TimeoutStartSec=120 TimeoutStopSec=120 [Install] WantedBy=multi-user.target
然后执行:
sudo systemctl daemon-reload
sudo systemctl enable flink
sudo systemctl start flink
sudo systemctl status flink
再检查:
jps
正常应该看到类似:
StandaloneSessionClusterEntrypoint TaskManagerRunner
最好再确认一下 /usr/local/flink 的权限:
ls -ld /usr/local/flink
理想情况是类似:
drwxr-xr-x ... wenhaoran wenhaoran ... /usr/local/flink
如果不是,可以改:
sudo chown -R wenhaoran:wenhaoran /usr/local/flink
另外,Java 路径一定要确认一下:
readlink -f $(which java)
如果结果不是:
/usr/lib/jvm/java-21-openjdk-amd64/bin/java
那 flink.service 里的 JAVA_HOME 要跟着改。
flink 我是新手 task 和 job 是什么关系
可以把 Flink 里的关系先记成一句话:
Job 是整个作业,Task 是这个作业真正跑起来后的执行单元。
结合你现在的水务实时预警项目会更容易理解。
假设你的代码大概是:
env.fromSource(kafkaSource, ...)
.map(...)
.keyBy(...)
.process(...)
.sinkTo(...);
env.execute("WaterAlarmJob");
这里执行以后,Flink 会提交一个 Job:
WaterAlarmJob
它代表"这一整套实时计算流程"。
可以理解成:
一个 Job | +-- Kafka Source | +-- Map | +-- KeyBy | +-- Process | +-- Sink
这些步骤叫 Operator(算子)。
但是 Flink 真正运行时,并不是简单地"一个算子 = 一个 Task"。它会根据并行度,把算子拆成多个并行执行实例,也就是 Subtask。
比如你设置:
env.setParallelism(2);
那么 Kafka Source 可能有:
Source Subtask 0 Source Subtask 1
Process 也可能有:
Process Subtask 0 Process Subtask 1
Sink 也可能有:
Sink Subtask 0 Sink Subtask 1
所以可以把关系理解为:
Job | +-- Operator | | | +-- Subtask 0 | +-- Subtask 1 | +-- Operator | +-- Subtask 0 +-- Subtask 1
其中这些 Subtask 才是实际在 TaskManager 上运行的执行实例。
再用你比较熟悉的 Java/Spring 思路类比:
Job ≈ 一个完整业务流程 Operator ≈ 业务流程中的一个处理步骤 Subtask ≈ 这个处理步骤的一个并发执行实例
比如水务预警:
Kafka读取点位 ↓ JSON解析 ↓ 按 deviceId / pointCode 分组 ↓ 规则判断 ↓ 生成告警 ↓ 写数据库 / Kafka
这一整条:
WaterAlarmJob
是一个 Job。
其中:
Kafka Source 规则判断 Alarm Sink
是不同的 Operator。
如果规则判断并行度是 4:
规则判断 ├── Subtask 0 ├── Subtask 1 ├── Subtask 2 └── Subtask 3
这意味着可以同时有 4 个并行执行实例处理数据。
还有一个特别容易混淆的概念:TaskManager。
关系大概是:
Flink Cluster JobManager | | 调度 WaterAlarmJob | v TaskManager | +-- Slot | +-- Task / Subtask | +-- Slot +-- Task / Subtask
你之前遇到过:
Task Managers: 0 Total Task Slots: 0 Available Task Slots: 0
然后 Job 报:
NoResourceAvailableException Could not acquire the minimum required resources
本质就是:
Job 已经提交了 ↓ JobManager 准备调度 Task ↓ 但是没有 TaskManager ↓ 没有 Slot ↓ Task 没地方执行 ↓ Job 启动失败
这个关系非常重要。
再比如你现在启动:
/usr/local/flink/bin/start-cluster.sh
在 standalone 模式下一般会启动:
JobManager TaskManager
JobManager 是"大脑",负责:
接收 Job 拆分执行任务 调度 Task 失败恢复 Checkpoint 协调
TaskManager 是"工人",负责:
真正执行 Task/Subtask
Parallelism 和 Task 的关系
你之前也问过 parallelism。
比如:
env.setParallelism(4);
它不是说:
启动 4 个 Job
而是说:
一个 Job 中的算子,默认允许拆成 4 个并行 Subtask。
例如:
WaterAlarmJob
只有 1 个 Job。
但规则计算可能变成:
Rule Process ├── subtask 0 ├── subtask 1 ├── subtask 2 └── subtask 3
假设 Kafka 里:
device001 device002 device003 device004 device005 ...
经过:
.keyBy(PointValue::deviceId)
之后,Flink 会把不同 key 分发给不同 subtask:
Subtask 0 device001 device005 Subtask 1 device002 device006 Subtask 2 device003 device007 Subtask 3 device004 device008
这样多个设备的数据就能并行处理。
但同一个 key,例如:
device001
通常会一直进入同一个下游 Keyed Operator 的 subtask,这样状态才不会乱。
对你的水务规则判断特别重要,比如:
A_Phase_Voltage 连续 5 次 > 250 才报警
如果同一个点位的数据被随机扔到不同 Task:
Task 1 收到第1、3、5条 Task 2 收到第2、4条
那"连续 5 次"就没法正确统计。
所以通常会:
.keyBy(data -> data.getDeviceId() + ":" + data.getPointCode())
然后:
同一个 device + point ↓ 始终去同一个 subtask ↓ 这个 subtask 保存状态 ↓ 连续次数才能正确计算
这也是 Flink 的核心思想之一。
你目前作为新手,建议先记住这 5 个词的关系:
Job 整个 Flink 程序/作业 Operator Job 里面的计算步骤 例如 Source、Map、Process、Sink Parallelism 一个 Operator 有多少个并行执行实例 Subtask Operator 的某一个并行实例 TaskManager 真正运行这些 Subtask 的 JVM 进程
画成一张图就是:
Flink Cluster | +----------+----------+ | | JobManager TaskManager | | | +-- Slot | | | | | +-- Subtask | | | +-- Slot | | | +-- Subtask | +-- WaterAlarmJob | +-- Kafka Source | +-- Rule Process | +-- Alarm Sink
对于你现在这个项目,最值得继续理解的下一层其实是:
Job → Operator → Task/Subtask → Slot → TaskManager → Parallelism
把这几个关系搞清楚后,你再看 Flink Web UI 里的 Jobs / TaskManagers / Task Slots / Parallelism,基本就不会迷糊了。