从 Shell 脚本优雅地关闭 Spring Boot 应用程序

Posted

技术标签:

【中文标题】从 Shell 脚本优雅地关闭 Spring Boot 应用程序【英文标题】:Gracefully Shutdown Spring Boot Application from Shell Script 【发布时间】:2016-06-17 16:49:12 【问题描述】:

我有一个使用可执行 jar 文件运行的 Spring Boot 应用程序。目前要停止服务,我们只是杀死进程。我看到我们可以使用以下方法优雅地关闭应用程序。

ApplicationContext ctx = SpringApplication.run(Application.class, args);

然后在代码的某处我可以调用ctx.close() 或者我们可以使用下面的静态方法。

SpringApplication.exit(ApplicationContext, ExitCodeGenerator)

它目前对我们有用,但我们实际上是在控制器中调用这个 ctx.close() 方法,如下所示。

@RequestMapping("/shutdownSpringBoot")
public void shutdownApplication() 
    MyApplication.ctx.close(); // I'm saving the context returned by spring boot in a static variable inside my main class

当我们通过 http 访问这个控制器方法时,应用程序会正常关闭。但我们不想这样做。是否可以编写一个 shell / 批处理脚本来触发我可以在其中调用 ctx.close() 方法的 java 类?我们正在寻找一个关闭脚本,就像我们从独立的 tomcat 容器(shutdown.bat / shutdown.sh)中获得的那样,这样我们就可以将我们的应用程序作为 jar 文件提供给我们的客户,他们可以通过以下方式启动或停止应用程序执行那些脚本。 (他们已经习惯了)。

谢谢, 桑杰

【问题讨论】:

发送一个 SIGINT 优雅并启动你上面描述的。您的停止脚本可能是一个单行程序,它读取 application.pid 并将其传递给 kill。 【参考方案1】:

我使用 PID 文件编写器写入文件并将 Jar 和 Pid 存储在与应用程序名称同名的文件夹中,并且 shell 脚本也具有相同的名称,并以 start 和 stop 结尾。

主类

@SpringBootApplication
public class MyApplication 

    public static final void main(String[] args) 
        SpringApplicationBuilder app = new SpringApplicationBuilder(MyApplication.class);
        app.build().addListeners(new ApplicationPidFileWriter());
        app.run();
    

YML 文件

spring.pid.fail-on-write-error: true
spring.pid.file: /server-path-with-folder-as-app-name-for-ID/appName/appName.pid

这里是启动脚本(start-appname.sh):

shellScriptFileName=$(basename -- "$0")
shellScriptFileNameWithoutExt="$shellScriptFileName%.*"
appName=$shellScriptFileNameWithoutExt:6

PROCESS=$1
PIDS=`ps aux |grep [j]ava.*$appName.*jar | awk 'print $2'`
if [ -z "$PIDS" ]; then
  echo "No instances of $appName is running..." 1>&2
else
  for PID in $PIDS; do
    echo "Waiting for the process($PID) to finish on it's own for 3 mins..."
    sleep 3m
    echo "FATAL:Killing $appName with PID:$PID."
    kill -9 $PID
  done
fi

# Preparing the java home path for execution
JAVA_EXEC='/usr/bin/java'

# Java Executable - Jar Path Obtained from latest file in directory
JAVA_APP=$(ls -t /server-path-with-folder-as-app-name/$appName/$appName*.jar | head -n1)

# JVM Parameters and Spring boot initialization parameters
JVM_PARAM="-Xms512m -Xmx1024m -Dspring.profiles.active=sit -Dcom.webmethods.jms.clientIDSharing=true"

# To execute the application.
FINAL_EXEC="$JAVA_EXEC $JVM_PARAM -jar $JAVA_APP"

# Making executable command using tilde symbol and running completely detached from terminal
`nohup $FINAL_EXEC  </dev/null >/dev/null 2>&1 &`

echo "$appName has been started successfully."

这里是停止脚本(stop-appname.sh):

shellScriptFileName=$(basename -- "$0")
shellScriptFileNameWithoutExt="$shellScriptFileName%.*"
appName=$shellScriptFileNameWithoutExt:5

# Script to stop the application
PID_PATH="server-path-with-folder-as-app-name-for-PID/$appName/$appName.pid"

if [ ! -f "$PID_PATH" ]; then
   echo "Process Id FilePath($PID_PATH) Not found"
else
pid=`cat $PID_PATH`
    if [ ! -e /proc/$pid -a /proc/$pid/exe ]; then
        echo "$appName was not running.";
    else
       kill $pid;
       echo "Gracefully stopping $appName with PID:$pid..."
    fi
fi

【讨论】:

【参考方案2】:

您可以创建一个shell脚本并直接关闭springboot应用程序。只需确保将关闭挂钩注册到您的 Spring Boot 应用程序即可。在关闭挂钩中,您可以关闭上下文。你可以查看我的回答here

【讨论】:

【参考方案3】:

我认为您可以通过检查命令行参数来简单地检测到您正在将 jar 作为命令行实用程序运行,而不是加载整个 Spring 上下文并启动 Web 应用程序,您只需使用 HTTP 访问本地 shutdownSpringBoot URL内置 Java HTTP 客户端。

【讨论】:

以上是关于从 Shell 脚本优雅地关闭 Spring Boot 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 优雅地关闭应用程序

为啥 Spring Context 没有优雅地关闭?

如何通过start-stop-daemon优雅地关闭Spring Boot应用程序[重复]

sh shell脚本中的前向声明通过包含一个处理执行顺序的主函数up-top来最优雅地处理

Spring 优雅关闭 - 不支持请求方法发布

spring-boot 2 优雅关闭网络