The premise

As a Java developer, I will use SpringBoot to develop Web applications in many scenarios. At present, SpringCloud family bucket is also built based on SpringBoot. When SpringBoot applications are deployed on servers, o&M management scripts need to be written. Based on experience, this paper attempts to summarize Shell scripts used in previous production and write a reusable SpringBoot application operation and maintenance script, so as to greatly reduce the workload of SpringBoot application startup, status, restart and other management. The Shell script in this article works fine on CentOS7, not necessarily on other operating systems. If you are not interested in the basics or principles, you can leave it to the end and copy the script.

Shell-related knowledge of dependencies

In addition to the basic Shell syntax to be relatively proficient in writing SpringBoot application operation and maintenance scripts, there are two more important problems to be solved (in my opinion) :

  • Get the target application’s process correctlyIDThat is, getProcess ID(the following saysPID).
  • killCommand the correct use of posture.
  • The commandnohupThe correct way to use.

For the PID

In general, if the PID can be successfully obtained by the application name, you can determine that the application process is running. Otherwise, the application process is not running. The running status of an application process is determined based on the PID. Therefore, the command to obtain the PID is invoked several times in the application process management script. Normally, the grep command is used to find the PID. For example, the following command is used to query the PID of the Redis service:

ps -ef |grep redis |grep -v grep |awk '{print $2}'
Copy the code

This is actually a compound command, every | behind is a complete independence of command, including:

  • ps -efispsThe command and-efParameters,psThe command is used to view the status of processes.-eRepresents shows all processes, while-fRepresents the complete output that shows parent-child relationships between processes, such as the following in my virtual machineCentOS 7performps -efAfter the result:

  • grep XXXIn fact, isgrepThe corresponding target parameter is used to search the result of the target parameter. The compound command will search the result of the previous command.
  • grep -v grepisgrepThe command is ignoredgrepIts own process.
  • awk '{print $2}'I’m just going to take the second column.

Ps – ef | grep redis | grep -v grep | awk ‘{print $2}’ compound command execution process is:

  • < 1 >throughps -efObtain the system process status.
  • < 2 >throughgrep redisfrom< 1 >Result search inredisKeyword, getredisProcess information.
  • < 3 >throughgrep -v grepfrom< 2 >Filter out the results ingrepIts own process.
  • < 4 >throughawk '{print $2}'from< 3 >Gets the second column.

In a Shell script, you can get the PID in this way:

PID=`ps -ef |grep redis-server |grep -v grep |awk '{print $2}'`
echo $PID
Copy the code

One problem with this, however, is that you have to use this very long command every time you want to get a PID, which is a little unwieldy. You can use eval to simplify this process:

PID_CMD="ps -ef |grep docker |grep -v grep |awk '{print \$2}'"
PID=$(eval $PID_CMD)
echo $PID
Copy the code

Get the PID problem solved, and then decide what to do based on whether the PID exists.

Understand the kill command

The common form of the kill command is kill -n PID. The essential function of the kill command is to send a signal to the process corresponding to the PID, and the process corresponding to the PID needs to respond to the signal. The number of the signal is N.

The most common ones are 9) SIGKILL and 15) SIGTERM, which are generally described as follows:

The signal number The signal name describe function impact
15 SIGTERM Termination (ANSI) The system sends one to the corresponding processSIGTERMsignal The process stops immediately, either after releasing resources, or due to waitingIOTo continue running, there is usually a blocking process, or to put it another way, the process can be blocked, processed, or ignoredSIGTERMsignal
9 SIGKILL Kill(can't be caught or ignored) (POSIX) The system sends one to the corresponding processSIGKILLsignal SIGKILLThe signal should not be ignored, usually as the process stops immediately (although there are additional cases).

The default kill command without -n is kill -15. Generally speaking, the kill -9 PID is mandatory for a process. However, it may affect the process of releasing resources before the process ends or interrupt I/O operations, causing abnormal data loss.

The nohup command

If you want to keep the application process after you exit the account or close the terminal, run the nohup command to run the corresponding process.

Nohup is short for “no hang up,” which translates to “no hang,” and its function is to run commands without hanging.

The format of the nohup Command is: nohup Command [Arg…] [&], the function is to run a Command based on the Command and the optional additional parameter Arg, ignoring the SIGHUP signal in all kill commands, and the & symbol indicates that the Command needs to be run in the background.

Note that there are three standard streams commonly used in operating systems: 0: standard input stream STDIN 1: standard output stream STDOUT 2: standard error stream STDERR

If you run nohup Command & directly, all STDOUT and error output streams will be output to the current directory nohup. Out file. If you run nohup Command & directly, all STDOUT and error output streams will be output to the current directory nohup. For example, nohup Command 1>server.log 2>server.log &. But since STDERR has no buffer, this will cause server.log to be opened twice, causing STDERR and error output to compete and overwrite each other, so STDERR will be redirected to STDOUT, which is already open. The standard output stream STDOUT can omit the 1 before the > stream, so:

Change nohup Command 1>server.log 2>server.log & to nohup Command >server.log 2> &1&

However, when Java applications are deployed, the application will print logs to a specific directory on the disk for ELK collection. For example, the operation and maintenance of my former company requires that logs must be printed in /data/log-center/${serverName}. The standard output streams STDOUT and STDERR of nohup must be ignored completely. A possible solution would be to redirect both standard streams to “black hole /dev/null”. Such as:

nohup Command >/dev/null 2>&1 &

Write operation and maintenance scripts for SpringBoot applications

A SpringBoot application is essentially a Java application, but it is possible to add specific SpringBoot parameters. The following steps will analyze how to write a reusable operation and maintenance script.

The global variable

In order to reuse variables as much as possible and to improve the brevity of the script, we first extract reusable global variables. First we define the JDK location JDK_HOME:

JDK_HOME = "/ usr/lib/JVM/Java -- 1.8.0 comes with its - 1.8.0.242. B08-0. El7_7. X86_64 / bin/Java"Copy the code

Then define the APP_LOCATION of the application:

APP_LOCATION="/data/shell/app.jar"
Copy the code

Then define the application name APP_NAME (mainly for search and display) :

APP_NAME="app"
Copy the code

PID_CMD = PID_CMD = PID_CMD = PID_CMD

PID_CMD="ps -ef |grep $APP_LOCATION |grep -v grep |awk '{print \$2}'"
// PID = $(eval $PID_CMD)
Copy the code

Define vm attributes VM_OPTS:

VM_OPTS="-Xms2048m -Xmx2048m"
Copy the code

Define the SpringBoot property SPB_OPTS (typically used to configure the boot port, application Profile, registry address, etc.) :

SPB_OPTS="--spring.profiles.active=dev"
Copy the code

These parameters can be modified or added based on actual scenarios.

Writing core methods

For example, if the script file is server.sh, run sh server.sh Command.

  • start: Starts the service.
  • info: Prints information, mainly sharing the contents of variables.
  • status: Displays the service status to determine whether the service is running.
  • stop: Stops the service process.
  • restart: Restarts the service.
  • help: Help guide.

Here the specific method to call is determined by the case keyword and the first argument entered at command execution.

start() {
 echo "start: start server"
}

stop() {
 echo "stop: shutdown server"
}

restart() {
 echo "restart: restart server"
}

status() {
 echo "status: display status of server"
}

info() {
 echo "help: help info"
}

help() {
   echo "start: start server"
   echo "stop: shutdown server"
   echo "restart: restart server"
   echo "status: display status of server"
   echo "info: display info of server"
   echo "help: help info"
}

case $1 in
start)
    start
    ;;
stop)
    stop
    ;;
restart)
    restart
    ;;
status)
    status
    ;;
info)
    info
    ;;
help)
    help
    ;;
*)
    help
    ;;
esac
exit $?
Copy the code

Test it out:

[root@localhost shell]# sh server.sh 
start: start server
stop: shutdown server
restart: restart server
status: display status of server
info: display info of server
help: help info
......
[root@localhost shell]# sh c.sh start
start: start server
Copy the code

Then you need to write the corresponding method implementation.

Method the info

Info () is used to print environment variables for the current service, information about the service, and so on.

info() {
  echo "=============================info=============================="
  echo "APP_LOCATION: $APP_LOCATION"
  echo "APP_NAME: $APP_NAME"
  echo "JDK_HOME: $JDK_HOME"
  echo "VM_OPTS: $VM_OPTS"
  echo "SPB_OPTS: $SPB_OPTS"
  echo "=============================info=============================="
}
Copy the code

Method the status

The status() method is mainly used to show the running status of the service.

status() {
  echo "=============================status==============================" 
  PID=$(eval $PID_CMD)
  if [[ -n $PID ]]; then
       echo "$APP_NAME is running,PID is $PID"
  else
       echo "$APP_NAME is not running!!!"
  fi
  echo "=============================status=============================="
}
Copy the code

The start method

The start() method is used to start services, using JDK and nohup commands.

start() { echo "=============================start==============================" PID=$(eval $PID_CMD) if [[ -n $PID ]];  then echo "$APP_NAME is already running,PID is $PID" else nohup $JDK_HOME $VM_OPTS -jar $APP_LOCATION $SPB_OPTS >/dev/null 2>\$1 & echo "nohup $JDK_HOME $VM_OPTS -jar $APP_LOCATION $SPB_OPTS >/dev/null 2>\$1 &" PID=$(eval $PID_CMD) if [[ -n $PID ]]; then echo "Start $APP_NAME successfully,PID is $PID" else echo "Failed to start $APP_NAME !!!" fi fi echo "=============================start==============================" }Copy the code
  • Check whether the application is running. If yes, you can obtain the application processPID, then return directly.
  • usenohupWith commandjava -jarCommand to start the applicationjarPackage, based onPIDCheck whether the startup is successful.

Stop method

The stop() method is used to terminate an application process. In order to kill the process safely and gracefully, use kill -15 first. Ensure that the process cannot be killed by kill -15, and then use kill -9.

stop() {
 echo "=============================stop=============================="
 PID=$(eval $PID_CMD)
 if [[ -n $PID ]]; then
    kill -15 $PID
    sleep 5
    PID=$(eval $PID_CMD)
    if [[ -n $PID ]]; then
      echo "Stop $APP_NAME failed by kill -15 $PID,begin to kill -9 $PID"
      kill -9 $PID
      sleep 2
      echo "Stop $APP_NAME successfully by kill -9 $PID"
    else 
      echo "Stop $APP_NAME successfully by kill -15 $PID"
    fi 
 else
    echo "$APP_NAME is not running!!!"
 fi
 echo "=============================stop=============================="
}
Copy the code

Restart method

Stop () and then start().

restart() {
  echo "=============================restart=============================="
  stop
  start
  echo "=============================restart=============================="
}
Copy the code

test

Jar: /data/shell: /data/shell: /data/shell: /data/shell: /data/shell: /data/shell: /data/shell: /data/shell: /data/shell: /data/shell

/data/shell
  - app.jar
  - server.sh
Copy the code

The results of one test were as follows:

[root@localhost shell]# sh server.sh info =============================info============================== APP_LOCATION: /data/shell/app.jar APP_NAME: app JDK_HOME: /usr/lib/jvm/java-1.8.0-openJDK -1.8.0.242.b08-0.el7_7.x86_64/bin/java VM_OPTS: -xms2048m -xmx2048m SPB_OPTS: --spring.profiles.active=dev =============================info============================== ...... [root@localhost shell]# sh server.sh start =============================start============================== app is already running,PID is 26950 =============================start============================== ...... [root@localhost shell]# sh server.sh stop =============================stop============================== Stop app successfully by kill -15 =============================stop============================== ...... [root@localhost shell]# sh server.sh restart =============================restart============================== =============================stop============================== app is not running!!! =============================stop============================== =============================start============================== Start app successfully,PID is 27559 =============================start============================== =============================restart============================== ...... [root@localhost shell]# curl http://localhost:9091/ping -s [root@localhost shell]# pongCopy the code

The test script verifies that the execution results are correct. Among them, ================= is intentionally added by the author, and can be removed if you feel an eyesore.

summary

SpringBoot is the mainstream framework of Web services at present or in the future for a long time. The author spent some time to learn Shell related syntax, combined with Nohup, PS and other Linux commands to write a reusable application operation and maintenance script, which has been applied in the test and production environment, saving operation and maintenance costs to a certain extent.

References:

  • Nohup Invocation

The appendix

Here is everything in the server.sh script:

#! /bin/bashJDK_HOME = "/ usr/lib/JVM/Java -- 1.8.0 comes with its - 1.8.0.242. B08-0. El7_7. X86_64 / bin/Java" VM_OPTS = "- Xms2048m - Xmx2048m" SPB_OPTS="--spring.profiles.active=dev" APP_LOCATION="/data/shell/app.jar" APP_NAME="app" PID_CMD="ps -ef |grep $APP_NAME |grep -v grep |awk '{print \$2}'" start() { echo "=============================start==============================" PID=$(eval $PID_CMD) if [[ -n $PID ]]; then echo "$APP_NAME is already running,PID is $PID" else nohup $JDK_HOME $VM_OPTS -jar $APP_LOCATION $SPB_OPTS >/dev/null 2>\$1 & echo "nohup $JDK_HOME $VM_OPTS -jar $APP_LOCATION $SPB_OPTS >/dev/null 2>\$1 &" PID=$(eval $PID_CMD) if [[ -n $PID ]]; then echo "Start $APP_NAME successfully,PID is $PID" else echo "Failed to start $APP_NAME !!!" fi fi echo "=============================start==============================" } stop() { echo "=============================stop==============================" PID=$(eval $PID_CMD) if [[ -n $PID ]]; then kill -15 $PID sleep 5 PID=$(eval $PID_CMD) if [[ -n $PID ]]; then echo "Stop $APP_NAME failed by kill -15 $PID,begin to kill -9 $PID" kill -9 $PID sleep 2 echo "Stop $APP_NAME successfully by kill -9 $PID" else echo "Stop $APP_NAME successfully by kill -15 $PID" fi else echo "$APP_NAME is not running!!!" fi echo "=============================stop==============================" } restart() { echo "=============================restart==============================" stop start echo "=============================restart==============================" } status() { echo "=============================status==============================" PID=$(eval $PID_CMD) if [[ -n $PID ]]; then echo "$APP_NAME is running,PID is $PID" else echo "$APP_NAME is not running!!!" fi echo "=============================status==============================" } info() { echo "=============================info==============================" echo "APP_LOCATION: $APP_LOCATION" echo "APP_NAME: $APP_NAME" echo "JDK_HOME: $JDK_HOME" echo "VM_OPTS: $VM_OPTS" echo "SPB_OPTS: $SPB_OPTS" echo "=============================info==============================" } help() { echo "start: start server" echo "stop: shutdown server" echo "restart: restart server" echo "status: display status of server" echo "info: display info of server" echo "help: help info" } case $1 in start) start ;; stop) stop ;; restart) restart ;; status) status ;; info) info ;; help) help ;; *) help ;; esac exit $?Copy the code

Personal blog

  • Throwable’s Blog

Irregular update, only write original, biased towards architecture, concurrency.

(C-2-D E-A-2020-03-01)

Technical official account (Throwable Digest), push the author’s original technical articles from time to time (never plagiarize or reprint) :