• linux环境中Java服务通过shell脚本重启(升级)自己


    今天遇到一个远程升级的需求,通过接口去触发终端服务的接口,重新拉取最新的jar包,并重启终端服务,这个终端服务是用java写的。 实现该需求,两个步骤,一个是需要一个shell脚本:拉取jar包、kill掉服务、启动服务;还有一个就是java中收到消息去调用shell脚本。

    脚本

    启动命令:

    /root/dtest/upgrade.sh jar-name 端口 jar下载地址 jar存放路径
     1 # !/bin/bash
     2 echo "start upgrade......"
     3 ## 判断参数是否正确
     4 ########### jar包名称 ############
     5 APPLICATION_NAME=""
     6 if [ ! $1 ]; then
     7   echo "待执行的jar名称 IS NULL"
     8   exit 1
     9 else
    10     APPLICATION_NAME=$1".jar"
    11 fi
    12 SERVER_PORT=""
    13 if [ ! $2 ]; then
    14   echo "端口 IS NULL"
    15   exit 1
    16 else
    17     SERVER_PORT=$2
    18 fi
    19 ########### 软件包下载地址 ############
    20 FILE_URL=""
    21 if [ ! $3 ]; then
    22   echo "软件包下载地址 IS NULL"
    23   exit 1
    24 else
    25     FILE_URL=$3
    26 fi
    27 BASE_PATH=""
    28 if [ ! $4 ]; then
    29   echo "软件包下载地址 IS NULL"
    30   BASE_PATH="/usr/local/docker"
    31 else
    32   BASE_PATH=$4
    33 fi
    34 ## kill 掉进程
    35 PROCESS=`ps -ef|grep $APPLICATION_NAME|grep -v grep|grep -v PPID|awk '{ print $2}'`
    36 for i in $PROCESS
    37 do
    38   echo "停止服务:kill the $APPLICATION_NAME process [ $i ]"
    39   kill -9 $i
    40 done
    41 ##备份
    42 rm -rf $APPLICATION_NAME
    43 ## 下载应用服务包
    44 echo "download the application package"
    45 echo "升级包下载命令 wget $FILE_URL -O "/root/dtest/"$APPLICATION_NAME"
    46 wget $FILE_URL -O "/root/dtest/"$APPLICATION_NAME
    47 echo "升级包下载完成!!!"
    48 ## 启动服务
    49 echo "开始启动 $1 服务"
    50 chmod 777 $APPLICATION_NAME
    51 nohup java -server -jar -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -Xms256m -Xmx512m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC $BASE_PATH"/"$APPLICATION_NAME --server.port=$SERVER_PORT   2>&1 &
    52 echo "服务启动命令:java -server -jar -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -Xms256m -Xmx512m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC $BASE_PATH"/"$APPLICATION_NAME --server.port=$SERVER_PORT > /dev/null 2>&1 &"
    53 for st in $(seq 1 20)
    54     do
    55 #        PID=$(netstat -nlp | grep :$EXECUTE_PORT | awk '{print $7}' | awk -F"/" '{ print $1 }');
    56         PID=$(netstat -nlp | grep :$SERVER_PORT | awk '{print $7}' |sed 's/([0-9]*).*/1/g');
    57         if [ $st -eq 20 ] && [ -z "$PID" ]; then
    58             echo "服务启动失败" ### $PID 为空
    59             break
    60         fi
    61 
    62         if [ -z "$PID" ]; then
    63             sleep 2
    64             echo $st"服务启动中...." ### $PID 为空
    65         else
    66             echo "服务名称:$APPLICATION_NAME ,端口号为:$SERVER_PORT ,进程号为:$PID 启动成功 , 耗时:$[$[st-1]*3] seconds!!!"
    67             break
    68         fi
    69     done

    Java程序

    java调用shell脚本有多种方式,简单粗暴的方式是:Runtime.getRuntime().exec() 

    但现实给我上了一课,当kill掉自己服务后,后面的脚本也停止执行了,原因处在,当服务执行自身重启的命令时,父进程关闭导致管道连接中断,将导致子进程也崩溃,从而无法完成后续的启动。

    解决方式,

    1. 设置子进程IO输出重定向到指定文件
    2. 设置属性子进程的I/O源或目标将与当前进程的相同,两者相互独立

    上代码:(源码下载

     1 package com.dzh.boot.demo.controller;
     2 
     3 import org.springframework.beans.factory.annotation.Value;
     4 import org.springframework.web.bind.annotation.RequestMapping;
     5 import org.springframework.web.bind.annotation.RestController;
     6 
     7 import java.io.File;
     8 import java.io.IOException;
     9 
    10 /**
    11  * 升级当前服务 --- 拉取最新jar,杀掉当前服务,启动最新的jar
    12  * @date 2021.4.9
    13  */
    14 @RestController
    15 public class UpgradeController {
    16 
    17     //脚本的地址
    18     @Value("${my.test.scriptPath}")
    19     private String scriptPath;
    20 
    21     /**
    22      * jar包的名称  去掉.jar
    23      */
    24     @Value("${my.test.name}")
    25     private String applicationName;
    26 
    27     /**
    28      * 服务启动的端口
    29      */
    30     @Value("${server.port}")
    31     private String port;
    32 
    33     /**
    34      * 最新jar包的下载地址
    35      */
    36     @Value("${my.test.fileUrl}")
    37     private String fileUrl;
    38 
    39     /**
    40      * jar包放置的路径
    41      */
    42     @Value("${my.test.basePath}")
    43     private String basePath;
    44 
    45     /**
    46      * 触发升级
    47      * @return
    48      * @throws Exception
    49      */
    50     @RequestMapping("run")
    51     private String run() throws Exception {
    52         ProcessBuilder sh = new ProcessBuilder("sh", scriptPath, applicationName, port, fileUrl, basePath);
    53         asynExeLocalComand(null, sh);
    54         return "成功";
    55     }
    56 
    57     /**
    58      * 用来检查服务是否正常
    59      * @return
    60      * @throws IOException
    61      */
    62     @RequestMapping("getParam")
    63     private String getParam() throws IOException {
    64         return scriptPath + "  " + applicationName + "  " + fileUrl + "   " + basePath + "  " + port;
    65     }
    66 
    67 
    68 
    69     public static void asynExeLocalComand(File file, ProcessBuilder pb) throws IOException {
    70         // 不使用Runtime.getRuntime().exec(command)的方式,因为无法设置以下特性
    71         // Java执行本地命令是启用一个子进程处理,默认情况下子进程与父进程I/O通过管道相连(默认ProcessBuilder.Redirect.PIPE)
    72         // 当服务执行自身重启的命令时,父进程关闭导致管道连接中断,将导致子进程也崩溃,从而无法完成后续的启动
    73         // 解决方式,(1)设置子进程IO输出重定向到指定文件;(2)设置属性子进程的I/O源或目标将与当前进程的相同,两者相互独立
    74         if (file == null || !file.exists()) {
    75             // 设置属性子进程的I/O源或目标将与当前进程的相同,两者相互独立
    76             pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
    77             pb.redirectError(ProcessBuilder.Redirect.INHERIT);
    78             pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
    79         } else {
    80             // 设置子进程IO输出重定向到指定文件
    81             // 错误输出与标准输出,输出到一块
    82             pb.redirectErrorStream(true);
    83             // 设置输出日志
    84             pb.redirectOutput(ProcessBuilder.Redirect.appendTo(file));
    85         }
    86         // 执行命令进程
    87         pb.start();
    88     }
    89 
    90 }
  • 相关阅读:
    ubuntu 11.10下 配置环境变量 对 adb无效
    一个NB的博客 个人感觉非常有用
    SVN 错误提交配置文件,
    代码格式真的很重要
    图解DB2体系结构(转)
    DB2基本概念——实例,数据库,模式,表空间
    DB2 数据库安全总述
    DB2关于标识列(自增列)的对比试验、使用示例
    DB2的SQL编程(转)
    DB2 数据类型(转)
  • 原文地址:https://www.cnblogs.com/dingzuoheng/p/14637855.html
Copyright © 2020-2023  润新知