Java 应用程序作为系统服务

标签 java linux service centos7 init

我试图在 CentOS7 中将 java 应用程序作为系统服务运行。该 jar 应该与特定用户一起运行:appuser。我有一个 shell 脚本,它使用以下命令运行 jar。整个脚本要大得多,因为它还处理停止、重新启动和状态,但这是开始部分:

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 并写入 pid 文件。我在命令中使用 tail 来获取 PID,因为我实际上有 3 个进程:su、/bin/sh 和实际的 java -jar 命令。

现在我在 /etc/systemd/system/multi-user.target.wants/myservice.service 中也有一个 systemctl 脚本,它看起来像这样:

[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 后立即执行此操作是一种更安全的方法。

  • 服务类型是简单,但您的脚本将它作为一个单独的进程派生,然后返回。这会混淆 systemd,您的服务将无法启动。

这两个问题都可能很容易解决。我假设您只需要 PID 来停止服务,这样做就像向它发送 SIGINT 一样简单。在这种情况下,您可以利用以下事实:在没有 ExecStop 的情况下,systemd 将简单地向服务进程发送一个 SIGINT 来停止它。

  • 在 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 文件中配置。)

关于Java 应用程序作为系统服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50446537/

相关文章:

java - ElasticSearch (2.2) startDate 和 endDate 之间的 Java 过滤器(如果存在)

c++ - 在目标机器上访问 POSIX 信号量时可执行文件崩溃(SEGV_MAPERR)

linux - 来自另一个函数的变量不可见?

java - Android 加速计记录服务停止工作并且未调用 onDestroy

android - 手机关机时会调用onDestroy吗?

java - 无法加载资源,总是返回null

java - 无法从 google url 下拉 chromedriver 驱动程序

java - 如何从运行在 Linux 上的 Java 访问首选应用程序?

c++ - opengl 库在哪里存储在 ubuntu : i need this to mention in my make file 上

java - 致命异常 : android. app.RemoteServiceException Context.startForegroundService() 然后没有调用 Service.startForeground()