目录:

一、nohup详解

1、什么是nohup?

2、nohup案例

3、nohup和&两者的区别

二、利用systemd工具管理程序,并实现(以java为例)服务开机自启动

1、systemd工具介绍

①什么是service?

②什么是systemctl?

2、编写systemctl脚本

①编写简单的systemctl脚本

②JVM知识扩展:

③用systemctl命令,让服务实现开机启动

3、systemctl 实现启动、停止、重启、查看运行状态、开机启动、取消开机启动

 ①systemctl 的常用命令:

②顺便扩展一下service的各种管理命令:

三、利用sh脚本实现java服务开机自启动

1、创建sh脚本

①使用notepad++软件转换成unix格式

② server.sh 脚本代码如下:

③ sh脚本的功能说明:启动、停止、重启、查询状态、归档、调试、备份

2、编辑系统rc.local文件,让脚本加入系统开机启动

3、系统重启,查看效果

欢迎点赞收藏,有问题请在评论区告诉我~


一、nohup详解

1、什么是nohup?

nohup 命令运行由 Command参数和任何相关的 Arg参数指定的命令,忽略所有挂断(SIGHUP)信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示“and”的符号)到命令的尾部。

nohup 是 no hang up 的缩写,就是不挂断的意思

nohup命令:如果你正在运行一个进程,而且你觉得在退出帐户时该进程还不会结束,那么可以使用nohup命令。该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。

在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中

2、nohup案例

1.   nohup python3 main.py >> main.log 2> &1 &

在上面的例子中,0 – stdin (standard input),1 – stdout (standard output),2 – stderr (standard error) ;

2>&1是将标准错误(2)重定向到标准输出(&1),标准输出(&1)再被重定向输入到myout.file文件中。例如nohup command > myout.file 2>error.txt &   那么错误内容会输出到error.txt文件中

2. 0 22 * * * /usr/bin/python /home/pu/download_pdf/download_dfcf_pdf_to_oss.py > /home/pu/download_pdf/download_dfcf_pdf_to_oss.log 2>&1

这是放在crontab中的定时任务,晚上22点时候怕这个任务,启动这个python的脚本,并把日志写在download_dfcf_pdf_to_oss.log文件中

3、nohup和&两者的区别

& : 指在后台运行

nohup : 不挂断的运行,注意并没有后台运行的功能,,就是指,用nohup运行命令可以使命令永久的执行下去,和用户终端没有关系,例如我们断开SSH连接都不会影响他的运行,注意了nohup没有后台运行的意思;&才是后台运行

&是指在后台运行,但当用户推出(挂起)的时候,命令自动也跟着退出

那么,我们可以巧妙的吧他们结合起来用就是
nohup COMMAND &
这样就能使命令永久的在后台执行

例如:

1. sh test.sh &  
将sh test.sh任务放到后台 ,即使关闭xshell退出当前session依然继续运行,但标准输出和标准错误信息会丢失(缺少的日志的输出)

将sh test.sh任务放到后台 ,关闭xshell,对应的任务也跟着停止。
2. nohup sh test.sh  
将sh test.sh任务放到后台,关闭标准输入,终端不再能够接收任何输入(标准输入),重定向标准输出和标准错误到当前目录下的nohup.out文件,即使关闭xshell退出当前session依然继续运行。
3. nohup sh test.sh  & 
将sh test.sh任务放到后台,但是依然可以使用标准输入,终端能够接收任何输入,重定向标准输出和标准错误到当前目录下的nohup.out文件,即使关闭xshell退出当前session依然继续运行。

二、利用systemd工具管理程序,并实现(以java为例)服务开机自启动

1、systemd工具介绍

        说道systemd工具,不得不提到service命令。它们是Linux服务管理的两种方式service和systemctl。但还是有区别,可以这么说:systemctl基本上是的更强大的版本service。

①什么是service?

        service命令Redhat Linux兼容的发行版中用来控制系统服务的实用工具,它以启动、停止、重新启动和关闭系统服务,还可以显示所有系统服务的当前状态。

        service命令实现的原理其实是去/etc/init.d目录下,去执行脚本文件,init.d目录包含许多系统各种服务的启动和停止脚本。当Linux启动时,会寻找这些目录中的服务脚本。

②什么是systemctl?

        systemctl是一个systemd工具,主要负责控制systemd系统和服务管理器。

        在systemd管理体系中,被管理的deamon(守护进程)称作unit(单元),对于单元的管理是通过命令systemctl来进行控制的。unit表示不同类型的systemd对象,通过配置文件进行标识和配置;文件主要包含了系统服务、监听socket、保存的系统快照以及其它与init相关的信息。

2、编写systemctl脚本

①编写简单的systemctl脚本

编写 test.service 丢到/usr/lib/systemd/system目录下

[Unit] 
Description=test.jar包服务程序
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service] 
User=root
Group=root

ExecStart=/opt/jdk1.8.0_161/bin/java  -server -Xms1024m -Xmx2048m -Dspring.profiles.active=production  -jar /root/test.jar
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID

[Install] 
WantedBy=multi-user.target

②JVM知识扩展:

​/opt/jdk1.8.0_161/bin/java  -server -Xms1024m -Xmx2048m -Dspring.profiles.active=production  -jar /root/test.jar

JVM调优参数:

-Xms 最小堆的大小, 也就是当你的虚拟机启动后, 就会分配这么大的堆内存给你 
-Xmx 是最大堆的大小 ,当最小堆占满后,会尝试进行GC

JVM标准参数-server与-client参数的区别:

可以通过-server或-client设置jvm的运行参数。
  它们的区别是Server VM的初始堆空间会大一些,默认使用的是并行垃圾回收器,启动慢运行快。
Client VM相对来讲会保守一些,初始堆空间会小一些,使用串行的垃圾回收器,它的目标是为了让JVM的启
动速度更快,但运行速度会比Serverm模式慢些。
  JVM在启动的时候会根据硬件和操作系统自动选择使用Server还是Client类型的JVM。32位操作系统
  如果是Windows系统,不论硬件配置如何,都默认使用Client类型的JVM。
  如果是其他操作系统上,机器配置有2GB以上的内存同时有2个以上CPU的话默认使用server模式,否则
使用client模式。
  64位操作系统
  只有server类型,不支持client类型。

 把service后缀的文件放在指定目录中后,就可以用systemctl 命令来管理程序了!

#启动test.service服务
systemctl start test.service

#查看服务状态
systemctl status test.service

效果如下: 

 我这里试了试service test status,发现也可以,额,好吧


 

 我重启了一下,发现又用不了了:

The service command supports only basic LSB actions (start, stop, restart, try-restart, reload, force-reload, status). For other actions, please try to use systemctl.

它的意思是:服务命令只支持基本的LSB操作(启动、停止、重启、尝试重启、重载、强制重载、状态)。对于其他操作,请尝试使用systemctl。

(⊙o⊙)…

在你修改了.service文件后使用systemctl 管理命令,可能会出现警告信息:

Warning: test.service changed on disk. Run 'systemctl daemon-reload' to reload units.

这个时候就要重新加载systemctl配置了,然后就会应用最新的配置。

systemctl daemon-reload

  对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件。这个时候你就可以直接用systemctl命令来管理了。

③用systemctl命令,让服务实现开机启动


        到第②小节的步骤就已经可以用systemctl 管理命令来管理你自己的程序了,如果你想让该程序开机启动,就执行下面的命令(以test.service为例)。

sudo systemctl enable test


        上面的命令相当于在/etc/systemd/system目录添加一个符号链接,指向/usr/lib/systemd/system里面的test.service文件。
这是因为开机时,Systemd只执行  /etc/systemd/system目录里面的配置文件。这也意味着,如果把test.service文件不使用符号链接而是直接放到 /etc/systemd/system目录下,就可以实现开机启动。当然,在 /usr/lib/systemd/system如果没有放 test.service文件就用不了systemctl 命令来管理程序了

那么,利用systemd工具来实现开机自启动(以java为例),就大功告成了!

3、systemctl 实现启动、停止、重启、查看运行状态、开机启动、取消开机启动

 ①systemctl 的常用命令:

(注意:systemctl start test 也可以用 systemctl start test.seriver )

#启动test
systemctl start test
#停止test
systemctl stop test
#重启test
systemctl restart test
#查看状态test
systemctl status test
#如果需要开机启动
systemctl enable test
#如果需要取消开机启动
systemctl disable test
#杀进程,向正在运行的进程发出kill信号。
systemctl kill test
#查看test相关的service配置文件
systemctl cat test
##查看 multi-user.target 包含的所有服务
systemctl list-dependencies multi-user.target
#切换到另一个 target,shutdown.target 就是关机状态
sudo systemctl isolate shutdown.target

②顺便扩展一下service的各种管理命令:

service的常用方式:
1.格式:service <service>
打印指定服务<service>的命令行使用帮助。
2.格式:service <service> start
启动指定的系统服务<service>
3.格式:service <service> stop
停止指定的系统服务<service>
4.格式:service <service> restart
重新启动指定的系统服务<service>,即先停止(stop),然后再启动(start)。
5.格式:chkconfig --list
查看系统服务列表,以及每个服务的运行级别。
6.格式:chkconfig <service> on
设置指定服务<service>开机时自动启动。
7.格式:chkconfig <service> off
设置指定服务<service>开机时不自动启动。
8.格式:ntsysv
以全屏幕文本界面设置服务开机时是否自动启动。

打开redis命令:service redis start
关闭redis命令:service redis stop
设为开机启动:chkconfig redis on

三、利用sh脚本实现java服务开机自启动

1、创建sh脚本

请使用下列方式创建sh脚本:server.sh(脚本亲测好用,不用额外配置编辑)

①使用notepad++软件转换成unix格式

 notepad++官方下载地址

 使用notepad++打开文件---编辑---文档格式转换---转为unix---上传至linux

② server.sh 脚本代码如下:

#!/bin/bash
# log format function
function log(){
   loglevel="$1"
   shift
   if [ "$1" ];then case "$loglevel" in
       debug)
          ;; # echo -e "\033[35m[$(date "+%Y-%m-%d %H:%M:%S")] [DEBUG]\t${@}\033[0m" ;;
       info)
          echo -e "\033[32m[$(date "+%Y-%m-%d %H:%M:%S")] [INFO]\t${@}\033[0m" ;;
       warning)
          echo -e "\033[33m[$(date "+%Y-%m-%d %H:%M:%S")] [WARNING]\t${@}\033[0m" ;;
       error)
          echo -e "\033[31m[$(date "+%Y-%m-%d %H:%M:%S")] [ERROR]\t${@}\033[0m" ;;
       *)
          echo "${@}" ;;
     esac
   fi
}
 
# Load environment variables
if [ -f "/etc/profile" ]; then
   log debug "从 [/etc/profile] 加载环境变量."
   . /etc/profile
fi
if [ -f "$HOME/.bash_profile" ]; then
   log debug "从 [$HOME/.bash_profile]加载环境变量."
   . $HOME/.bash_profile
fi
if [ -f "$HOME/.bashrc" ]; then
   log debug "从 [$HOME/.bashrc]加载环境变量."
   . $HOME/.bashrc
fi
 
# Verify that the service is running as a normal user
if [ "$(whoami)" == "root" ]; then
   log warning "不推荐使用 [$(whoami)] 用户运行应用服务."
fi
 
# Verify the java command, otherwise exit directly
if [ ! "$(command -v java)" ];then
   log error "未找到java命令,必须先安装JDK或JRE并配置环境变量."; exit 1
fi
 
# Switch working directory and define variables
cd $(dirname $0)
basedir="$(pwd -P)"
jarfile="$(find $basedir -maxdepth 1 -type f -name "*.jar")"
jarnums="$(find $basedir -maxdepth 1 -type f -name "*.jar"|wc -l)"
logfile="$basedir/log/application.log"
logconf="$basedir/log/logrotate.cnf"
logstat="$basedir/log/logrotate.status"
jarback="$basedir/backup"
 
log debug  "basedir: [$basedir]"
log debug  "jarfile: [$jarfile]"
log debug  "jarnums: [$jarnums]"
log debug  "logfile: [$logfile]"
log debug  "logconf: [$logconf]"
log debug  "jarback: [$jarback]"
 
# check jar file exists
function check(){
   if [ "$jarnums" -lt "1" ];then
      log error "目录 [${basedir}] 下未找到.jar文件."; exit 1
   elif [ "$jarnums" -gt "1" ];then
      log warning "目录 [${basedir}] 下.jar文件不唯一"; exit 1
   else
      pid="$(ps -aux|grep "java"|grep "$jarfile"|grep -v "grep"|awk '{print $2}'|tr '\n' ' ')"
   fi
}
 
# status function
function status(){
   check
   if [ "$pid" ];then
      log info "应用服务 [$(basename $jarfile)] 状态: 运行中,PID:[ $pid]."
   else
      log info "应用服务 [$(basename $jarfile)] 状态: 未运行."
   fi
}
 
# start function
function start(){
   check
   if [ "$pid" ];then
      log warning "应用服务 [$(basename $jarfile)] 已经在运行中, PID: [ $pid]."
   else
      log info "应用服务 [$(basename $jarfile)] 正在启动..."
      log debug "创建应用服务日志目录"
      log debug "mkdir -p $basedir/log"
      mkdir -p "$basedir/log"
      log debug "nohup $(command -v java) -Xms256M -Xmx512M -jar $jarfile $@ >> $logfile 2>&1 &"
      nohup $(command -v java) -Xms256M -Xmx512M -jar $jarfile $@ >> $logfile 2>&1 &
      pid="$!"
      if [ "$pid" ];then
         log debug "应用服务 日志轮转开始."
         log debug "(while kill -0 $pid;do rotate;sleep 60;done) > /dev/null 2>&1 &"
         (while kill -0 $pid;do rotate;sleep 60;done) > /dev/null 2>&1 &
         log info "应用服务 [$(basename $jarfile)] 启动完成,PID: [ $pid ]"
         log warning "应用服务 [$(basename $jarfile)] 日志输出将在30秒后自动退出."
         sleep 3
         log debug "应用启动日志: tail -1f $logfile &"
         tail -1f $logfile &
         (sleep 30 && kill $!) > /dev/null 2>&1 &
         log debug "(sleep 60 && kill $!) > /dev/null 2>&1 &"
      fi
   fi
}
 
# stop function
function stop(){
   check
   if [ "$pid" ];then
      log info "应用服务 [$(basename $jarfile)] 停止中..."
      log debug "kill -9 $pid"
      kill -9 $pid > /dev/null 2>&1
      sleep 2 && echo
      log debug "应用服务 [$(basename $jarfile)] 已经停止"
   else
      log warning "应用服务 [$(basename $jarfile)] 似乎没有在运行,不需要停止"
   fi
}
 
# restart function
function restart(){
   stop
   sleep 2
   start "$@"
}
 
 
# console start function
function console(){
   check
   if [ "$pid" ];then
      log warning "应用服务 [$(basename $jarfile)] 已经在运行中, PID: [ $pid]."
   else
      log info "应用服务 [$(basename $jarfile)] 正在启动..."
      log warning "console 参数仅用于调试, 使用 [Ctrl + C] 终止调试进程."
      log debug "创建应用服务日志目录"
      log debug "mkdir -p $basedir/log"
      mkdir -p "$basedir/log"
      sleep 2
      log debug "cd $basedir && $(command -v java) -Xms256M -Xmx512M -jar $jarfile $@ 2>&1 | tee $logfile"
      cd $basedir && $(command -v java) -Xms256M -Xmx512M -jar $jarfile $@ 2>&1 | tee $logfile
   fi
}
 
# rotate function
function rotate(){
   log debug "创建应用服务日志目录"
   log debug "mkdir -p $basedir/log"
   mkdir -p "$basedir/log"
   if [ ! -e $logconf ];then
      log warning "日志轮转配置文件 [${logconf}] 不存在, 将创建此配置文件."
      log debug "${logfile} {\n  size 32M\n rotate 16\n  compress\n  missingok\n  dateext\n  dateformat -%Y%m%d-%s\n  copytruncate\n}"
      echo -e "${logfile} {\n  size 32M\n rotate 16\n  compress\n  missingok\n  dateext\n  dateformat -%Y%m%d-%s\n  copytruncate\n}" > $logconf
   fi
   if [ -e $logfile ];then
      log info "归档并压缩应用服务日志 [${logfile}]"
      log debug "/usr/sbin/logrotate -s $logstat $logconf"
      /usr/sbin/logrotate -s $logstat $logconf
      log info "归档并压缩日志文件 ${logfile} 完成."
      log debug "使用 [ll -h ${logfile}*] 命令查看归档日志文件"
   fi
}
 
# backup function
function backup(){
   if [ ! -d "$jarback" ];then
      log warning "创建jar包备份目录 [$jarback]"
      log debug "mkdir -p $jarback"
      mkdir -p $jarback
   fi
   jarbasename="$(basename $jarfile)"
   jarbackname="${jarbasename%.*}-$(date "+%Y%m%d-%H%M%S").${jarbasename##*.}"
   log debug "应用服务 备份jar包目录 [$jarback]"
   log debug "应用服务 备份jar包名称 [$jarback/$jarbackname]"
   log info "备份jar包文件 [$jarfile]"
   log info " ==> jar包备份目录 [$jarback]"
   cp $basedir/$jarbasename $jarback/$jarbackname
}
 
# help
function help(){
   log "" "使用说明:"
   log "" " $0 {start|stop|status|restart|console|backup|rotate} [options]"
   log "" " [options] : "
   log "" "   --spring.config.location=app.yml,app-ext.yml  # 指定服务配置文件为: app.yml,app-ext.yml"
   log "" "   --spring.profiles.active=development          # 指定服务配置环境为: development"
   log "" "   --server.port=8888                            # 指定服务配置端口为: 8888"
   log "" "   --server.servlet.context-path=/test           # 指定服务配置路径为: /text"
}
 
act="$1"
shift
arg="$(echo "$@"|sed 's/[ ]*=[ ]*/=/g')"
 
log debug "Script parameters [ $act ]"
 
case "$act" in
   start)
      start $arg ;;
   stop)
      stop ;;
   restart)
      restart $arg ;;
   status)
      status ;;
   rotate )
      rotate ;;
   console)
      console $arg ;;
   backup)
      backup ;;
   *)
    start $arg ;;
esac
 
exit 0

注意:文件都要设置为可执行文件:

sudo chmod 755 server.sh

因为脚本会自动获取当前目录可运行的jar包,所以在当前目录下请保持仅有一个包;

③ sh脚本的功能说明:启动、停止、重启、查询状态、归档、调试、备份

脚本功能介绍:

./server.sh             默认执行./server.sh start ========》启动当前目录java程序

./server.sh stop        默认执行./server.sh start ========》启动当前目录java程序

./server.sh restart     ========》重启程序

./server.sh status      ========》查询程序运行状态

./server.sh rotate      ========》归档日志

./server.sh console     ========》前台运行程序,[Ctrl + C] 会终止进程

./server.sh backup      ========》备份jar包文件

2、编辑系统rc.local文件,让脚本加入系统开机启动

在上面的第一步就可以使用脚本来管理程序了,接下来是让脚本加入到系统的开机启动

记录当前server.sh脚本的路径,编辑/etc/rc.d/rc.local。命令如下:

cd /etc/rc.d

vi rc.local

#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.

#就是说rc.local的程序已经启动了, 防止重复运行
touch /var/lock/subsys/local

#脚本的绝对路径
/root/server.sh

注意:rc.local文件要授权:sudo chmod 755 rc.local

3、系统重启,查看效果

sudo reboot

 查看java程序:

ps -ef | grep java

 可以看到:

大功告成! 

欢迎点赞收藏,有问题请在评论区告诉我~

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐