一、如何将 Java 服务注册为 Systemd 服务
假设你的 JAR 包路径为 /opt/myapp/myapp.jar,启动命令为 java -jar /opt/myapp/myapp.jar。
1. 创建 Service 文件
使用 root 用户或 sudo 创建 /etc/systemd/system/myapp.service(文件名自定义,后缀必须是 .service):
ini
[Unit]
Description=My Java Application
After=network.target
[Service]
Type=simple
User=youruser # 建议用非 root 用户运行
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -jar /opt/myapp/myapp.jar
ExecStop=/bin/kill -15 $MAINPID # 这一行通常不需要写,systemd 默认就是发 SIGTERM
Restart=on-failure
RestartSec=5
TimeoutStopSec=60 # 优雅关闭等待 60 秒,超时后强制 kill
# 可选:环境变量
Environment="JAVA_OPTS=-Xmx512m"
[Install]
WantedBy=multi-user.target关键点:
ExecStop不写时,systemd 默认向进程发送SIGTERM(即kill -15)。
TimeoutStopSec定义了等待进程退出的时间,超时后发送SIGKILL(kill -9)。这正是理想的“优雅再强杀”策略。
User指定运行用户,避免用 root 跑 Java 服务。
2. 重新加载 Systemd 配置
bash
sudo systemctl daemon-reload3. 启用开机自启(可选)
bash
sudo systemctl enable myapp4. 日常管理命令
bash
sudo systemctl start myapp # 启动
sudo systemctl stop myapp # 优雅停止(SIGTERM)
sudo systemctl restart myapp # 优雅重启(先 stop 再 start)
sudo systemctl status myapp # 查看状态和日志二、相比手动 ps + grep + kill 的好处
三、手动脚本 vs Systemd 的真实差距(重点)
你原来的脚本大致是:
bash
pid=$(ps -ef | grep xx.jar | grep -v grep | awk '{print $2}')
kill -9 $pid # 或 kill -15
java -jar xx.jar &这个脚本有几个隐藏问题:
如果另一个 Java 进程也包含
xx.jar字符串,可能杀掉错误的进程。kill -9跳过了 ShutdownHook,正在写的文件会损坏,未提交的数据库事务会丢失,临时文件不会删除。kill -15虽然好,但如果应用卡死,进程永远不退,你的脚本会直接启动新实例 → 端口冲突,新服务启动失败。无法确保旧进程完全退出(比如释放完端口)再启动新进程。
而 Systemd 的 restart 会:
发送
SIGTERM等待最多
TimeoutStopSec秒检查进程是否还在,如果还在则发送
SIGKILL确认旧进程完全退出后,才启动新进程(避免端口冲突)
四、迁移建议
如果你现在已经有大量服务器用脚本,不需要一次性全部改。可以按以下步骤平滑迁移:
先在测试机上创建一个
.service文件,指向你的 JAR 包。确保你的 Java 服务支持优雅关闭(即注册了 ShutdownHook,或 Spring Boot 默认支持)。
测试
systemctl stop:观察日志,确认服务能正常关闭、释放端口、执行清理代码。测试
systemctl restart:验证新旧交替是否平滑。观察
TimeoutStopSec:你的服务可能关闭较慢(例如正在处理长请求),适当调大该值(如 120 秒)。生产环境分批替换手动脚本。
五、如果你的 Java 服务没有 ShutdownHook
很多框架(Spring Boot、Quarkus、Vert.x 等)已经内置了优雅关闭的 Hook,你不需要额外写代码。
如果是个极简的 while(true) 主循环,你需要自己添加:
java
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("收到 SIGTERM,开始清理...");
// 关闭线程池、关闭连接、等待请求完成等
}));这样当 systemd 发送 SIGTERM 时,这个 Hook 就会执行。
总结
完全可以将服务注册为 Systemd 服务,并使用
systemctl restart替代你的手动脚本。这是 Linux 下管理后台服务的标准做法,效果远优于自己写
ps + kill。你原先用
kill -9的问题可以通过 Systemd 的TimeoutStopSec+SIGTERM来解决。只需写一个
.service文件,然后systemctl daemon-reload即可开启新世界。