java - 作为系统服务的 Java 应用程序
问题描述
我正在尝试在 CentOS7 中将 java 应用程序作为系统服务运行。jar 应该使用特定用户运行:appuser。我有一个使用以下命令运行 jar 的 shell 脚本。整个脚本要大得多,因为它还处理停止、重新启动和状态,但这是开始部分:
servicename="myservice"
user="appuser"
pid_file="/var/run/$servicename.pid"
get_pid_from_file() {
cat "$pid_file"
}
get_pids() {
(ps -ef | grep myjar | grep $user | grep -v grep | awk '{print $2}')
}
is_running() {
! [ -z "`get_pids`" ] || ([ -f "$pid_file" ] && ps `get_pid_from_file` > /dev/null 2>&1)
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
case "$2" in
*)
su -s /bin/sh $user -c "cd /app/myworkingdir ; java -jar myjar.jar >> /var/log/systemout.log 2>> /var/log/systemerr.log" &
pid=`ps -ef | grep myjar | grep $user | grep -v grep | awk '{print $2}' | tail -1`
echo $pid
echo $pid > $pid_file
当我从命令行运行脚本时,它将启动 jar 并写入 pidfile。我使用命令中的尾部来获取 PID,因为我实际上有 3 个进程:su、/bin/sh 和实际的 java -jar 命令。
现在我也有一个 systemctl 脚本/etc/systemd/system/multi-user.target.wants/myservice.service
,它看起来像这样:
[Unit]
Description=myservice
After=syslog.target
After=network.target
[Service]
Type=simple
WorkingDirectory=/app/myworkingdir/run
PIDFile=/var/run/myservice.pid
ExecStart=/app/myworkingdir/run/myscript.sh start
ExecStop=/app/myworkingdir/run/myscript.sh stop
User=appuser
Group=appgrp
Restart=always
RestartSec=30
StartLimitInterval=60
StartLimitBurst=5
TimeoutStartSec=30
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
问题是systemctl start myservice.service
无法正确启动服务。
我在跑步时看到了这一点journeyctl -xe
:
May 21 13:03:23 myserver.com systemd[1]: Starting my service...
-- Subject: Unit myservice.service has begun start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit myservice.service has begun starting up.
May 21 13:03:23 myserver.com polkitd[619]: Unregistered Authentication Agent for unix-process:19329:16
May 21 13:03:23 myserver.com myscript.sh[19335]: Already started
May 21 13:03:24 myserver.com myscript.sh[19345]: Stopping myscript.sh..
这是 /var/log/messages 中的日志记录:
[root@myserver run]# systemctl start myservice.service
May 21 13:34:00 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:34:00 myserver systemd: Started myservice.
May 21 13:34:00 myserver systemd: Starting myservice...
[root@myserver run]# May 21 13:34:00 ctor-app52 myscript.sh: Already started
May 21 13:34:00 myserver denver.sh: Stopping myscript.sh..
May 21 13:34:31 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:34:31 myserver systemd: Started myservice.
May 21 13:34:31 myserver systemd: Starting myservice...
May 21 13:34:31 myserver myscript.sh: Already started
May 21 13:34:31 myserver myscript.sh: Stopping myscript.sh..
May 21 13:35:01 myserver systemd: Started Session 122559 of user root.
May 21 13:35:01 myserver systemd: Starting Session 122559 of user root.
May 21 13:35:01 myserver su: (to appuser) root on none
May 21 13:35:01 myserver systemd: LPdenver.service holdoff time over, scheduling restart.
May 21 13:35:01 myserver systemd: Started myservice.
May 21 13:35:01 myserver systemd: Starting myservice...
May 21 13:35:01 myserver myscript.sh: Already started
May 21 13:35:01 myserver myscript.sh: Stopping myscript.sh..
May 21 13:35:31 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:35:31 myserver systemd: Started myservice.
May 21 13:35:31 myserver systemd: Starting myservice...
May 21 13:35:31 myserver myscript.sh: Already started
May 21 13:35:31 myserver myscript.sh: Stopping myscript.sh..
May 21 13:36:01 myserver systemd: myservice.service holdoff time over, scheduling restart.
May 21 13:36:01 myserver systemd: Started myservice.
May 21 13:36:01 myserver systemd: Starting myservice...
May 21 13:36:01 myserver myscript.sh: Already started
May 21 13:36:02 myserver myscript.sh: Stopping myscript.sh..
我究竟做错了什么?
解决方案
您正在.service
文件中指定用户,因此您不需要su
在脚本中使用任何魔法。
替换这个:
su -s /bin/sh $user -c "cd /app/myworkingdir ; java -jar myjar.jar >> /var/log/systemout.log 2>> /var/log/systemerr.log"
有了这个:
cd /app/myworkingdir
java -jar myjar.jar >> /var/log/systemout.log 2>> /var/log/systemerr.log"
此外,我在您的脚本中还发现了两个问题:
您正在搜索进程表以查找您的服务的 PID。如果有另一个正在运行的进程的命令行包含相同的字符,这可能会失败(系统最终可能会选择错误的进程来终止)。
echo $!>$pid_file
在启动 java 之后是一种更安全的方法。服务类型是
simple
,但您的脚本将其分叉为一个单独的进程,然后返回。这会使 systemd 感到困惑,并且您的服务将无法启动。
这两个可能很容易解决。我假设您只需要 PID 来停止服务,这样做就像发送一个SIGINT
. 在这种情况下,您可以利用这样一个事实,即在没有 的情况下ExecStop
,systemd 将简单地向SIGINT
服务进程发送 a 以停止它。
在 java 调用之后删除 & 号,并在其前面加上前缀
exec
。这样,java 进程将被 systemd 视为守护进程。在 java 调用之后完全放弃 PID 魔法。
在
.service
文件中,删除ExecStop
条目。相反,也在 下
[Service]
,添加SuccessExitStatus=143
。(当以信号终止时,JVM 将以非零退出状态退出,即 128 加上信号。默认情况下,systemd 将退出状态 143 视为失败;此条目将告诉 systemd 143 代表优雅退出。)
您可以更进一步,完全删除脚本:
- 将 java 命令行指定为
ExecStart
- 将完整路径传递给 java 二进制文件(由 报告
which java
) - 删除重定向:默认情况下,systemd 会将 stdout 和 stderr 重定向到日志。(这也可以在
.service
文件中配置。)
推荐阅读
- python - 使folium heatmap根据固定的数字密度改变区域的颜色
- android - 我在 android studio 的 Textview 中显示货币汇率时遇到问题
- python - ValueError:寻找关闭的文件在 PyPDF2 上工作并收到此错误
- arrays - 使用 iMAonArray 时出现“数组超出范围”
- sql - 选择另一列匹配记录 - Oracle SQL
- django - 如何在 Django 中注册标签
- node.js - ESLint 与 Mocha
- reactjs - 如何在 ReactJS 上使用嵌套对象数据?
- javascript - 无法初始化商店的状态
- c# - MediaPlayerElement 不在 PC 上播放声音,但在手机上播放