Chr*_*ris 97 linux spring spring-boot
在Spring Boot Document中,他们说'每个SpringApplication都会向JVM注册一个关闭钩子,以确保在退出时优雅地关闭ApplicationContext.
当我单击ctrl+cshell命令时,可以正常关闭应用程序.如果我在生产机器中运行应用程序,我必须使用该命令
java -jar ProApplicaton.jar.但是我无法关闭shell终端,否则会关闭进程.
如果我运行命令nohup java -jar ProApplicaton.jar &,我不能使用ctrl+c优雅地关闭它.
在生产环境中启动和停止Spring Boot应用程序的正确方法是什么?
Jea*_*ond 53
如果您正在使用执行器模块,则可以通过JMX或关闭应用程序来关闭应用程序HTTP(添加endpoints.shutdown.enabled=true到您的application.properties文件中).
/shutdown - 允许应用程序正常关闭(默认情况下不启用).
根据端点的暴露方式,敏感参数可用作安全提示.例如,敏感端点在访问时需要用户名/密码HTTP(如果未启用Web安全性,则只需禁用).
Jas*_*key 45
关于@ Jean-Philippe Bond的回答,
这是一个maven快速示例,供maven用户配置HTTP端点,使用spring-boot-starter-actuator关闭弹簧启动Web应用程序,以便您可以复制和粘贴:
1.Maven pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)
2.application.properties:
#No auth protected
endpoints.shutdown.sensitive=false
#Enable shutdown endpoint
endpoints.shutdown.enabled=true
Run Code Online (Sandbox Code Playgroud)
所有端点都列在这里:
3.发送一个post方法来关闭app:
curl -X POST localhost:port/shutdown
Run Code Online (Sandbox Code Playgroud)
如果您需要auth保护的关闭方法,您可能还需要
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)
配置细节:
Jen*_*ens 40
这是另一个选项,不需要您更改代码或暴露关闭端点.创建以下脚本并使用它们来启动和停止您的应用程序.
start.sh
#!/bin/bash
java -jar myapp.jar & echo $! > ./pid.file &
Run Code Online (Sandbox Code Playgroud)
启动您的应用并将进程ID保存在文件中
stop.sh
#!/bin/bash
kill $(cat ./pid.file)
Run Code Online (Sandbox Code Playgroud)
使用保存的进程ID停止您的应用
start_silent.sh
#!/bin/bash
nohup ./start.sh > foo.out 2> foo.err < /dev/null &
Run Code Online (Sandbox Code Playgroud)
如果您需要使用远程计算机或CI管道中的ssh启动应用程序,请使用此脚本来启动应用程序.直接使用start.sh可以让shell挂起.
例如.重新部署您的应用程序,您可以使用以下命令重新启动
sshpass -p password ssh -oStrictHostKeyChecking=no userName@www.domain.com 'cd /home/user/pathToApp; ./stop.sh; ./start_silent.sh'
Run Code Online (Sandbox Code Playgroud)
nav*_*872 27
您可以使springboot应用程序将PID写入文件,您可以使用pid文件停止或重新启动或使用bash脚本获取状态.要将PID写入文件,请使用ApplicationPidFileWriter向SpringApplication注册一个侦听器,如下所示:
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./bin/app.pid"));
application.run();
Run Code Online (Sandbox Code Playgroud)
然后编写一个bash脚本来运行spring boot应用程序.参考.
现在您可以使用该脚本来启动,停止或重新启动.
从 Spring Boot 2.3及更高版本开始,有一个内置的优雅关闭机制。
在 Spring Boot 2.3 之前,没有开箱即用的优雅关闭机制。一些 spring-boot 启动器提供了这个功能:
我是 nr 的作者。1. 启动器名为“Hiatus for Spring Boot”。它在负载平衡器级别工作,即简单地将服务标记为 OUT_OF_SERVICE,不会以任何方式干扰应用程序上下文。这允许进行正常关闭,并且意味着,如果需要,可以将服务停止服务一段时间,然后恢复运行。缺点是它不会停止 JVM,您必须使用kill命令来完成。当我在容器中运行所有东西时,这对我来说没什么大不了的,因为无论如何我都必须停止并移除容器。
2 号和 3 号或多或少基于安迪威尔金森的这篇文章。它们是单向工作的——一旦被触发,它们最终会关闭上下文。
所有答案似乎都缺少这样一个事实,即您可能需要在正常关机期间(例如,在企业应用程序中)以协调的方式完成某些工作。
@PreDestroy允许您在单个bean中执行关闭代码。更加复杂的东西看起来像这样:
@Component
public class ApplicationShutdown implements ApplicationListener<ContextClosedEvent> {
@Autowired ... //various components and services
@Override
public void onApplicationEvent(ContextClosedEvent event) {
service1.changeHeartBeatMessage(); // allows loadbalancers & clusters to prepare for the impending shutdown
service2.deregisterQueueListeners();
service3.finishProcessingTasksAtHand();
service2.reportFailedTasks();
service4.gracefullyShutdownNativeSystemProcessesThatMayHaveBeenLaunched();
service1.eventLogGracefulShutdownComplete();
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
Spring Boot在尝试创建应用程序上下文时提供了几个应用程序侦听器,其中之一是ApplicationFailedEvent。我们可以用来了解应用程序上下文是否已初始化。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.context.ApplicationListener;
public class ApplicationErrorListener implements
ApplicationListener<ApplicationFailedEvent> {
private static final Logger LOGGER =
LoggerFactory.getLogger(ApplicationErrorListener.class);
@Override
public void onApplicationEvent(ApplicationFailedEvent event) {
if (event.getException() != null) {
LOGGER.info("!!!!!!Looks like something not working as
expected so stoping application.!!!!!!");
event.getApplicationContext().close();
System.exit(-1);
}
}
}
Run Code Online (Sandbox Code Playgroud)
将上述侦听器类添加到SpringApplication。
new SpringApplicationBuilder(Application.class)
.listeners(new ApplicationErrorListener())
.run(args);
Run Code Online (Sandbox Code Playgroud)
SpringApplication 向 JVM 隐式注册了一个关闭钩子,以确保 ApplicationContext 在退出时正常关闭。这也将调用所有用@PreDestroy. 这意味着我们不必像在 spring 核心应用程序中那样在引导应用程序中显式使用 a 的 registerShutdownHook()方法ConfigurableApplicationContext。
@SpringBootConfiguration
public class ExampleMain {
@Bean
MyBean myBean() {
return new MyBean();
}
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(ExampleMain.class, args);
MyBean myBean = context.getBean(MyBean.class);
myBean.doSomething();
//no need to call context.registerShutdownHook();
}
private static class MyBean {
@PostConstruct
public void init() {
System.out.println("init");
}
public void doSomething() {
System.out.println("in doSomething()");
}
@PreDestroy
public void destroy() {
System.out.println("destroy");
}
}
}
Run Code Online (Sandbox Code Playgroud)
我没有暴露任何端点并启动(在后台使用nohup,没有通过nohup创建的文件)并使用shell脚本停止(优先使用KILL PID并在3分钟后仍然运行应用程序时强制终止).我只是创建可执行jar并使用PID文件编写器来编写PID文件并将Jar和Pid存储在与应用程序名称相同的文件夹中,并且shell脚本也具有相同的名称,最后是start和stop.我称这些停止脚本并通过jenkins管道启动脚本.到目前为止没有问题.完美适用于8个应用程序(非常通用的脚本,易于应用于任何应用程序).
主类
@SpringBootApplication
public class MyApplication {
public static final void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(MyApplication.class);
app.build().addListeners(new ApplicationPidFileWriter());
app.run();
}
}
Run Code Online (Sandbox Code Playgroud)
YML文件
spring.pid.fail-on-write-error: true
spring.pid.file: /server-path-with-folder-as-app-name-for-ID/appName/appName.pid
Run Code Online (Sandbox Code Playgroud)
这是启动脚本(start-appname.sh):
#Active Profile(YAML)
ACTIVE_PROFILE="preprod"
# JVM Parameters and Spring boot initialization parameters
JVM_PARAM="-Xms512m -Xmx1024m -Dspring.profiles.active=${ACTIVE_PROFILE} -Dcom.webmethods.jms.clientIDSharing=true"
# Base Folder Path like "/folder/packages"
CURRENT_DIR=$(readlink -f "$0")
BASE_PACKAGE="${CURRENT_DIR%/bin/*}"
# Shell Script file name after removing path like "start-yaml-validator.sh"
SHELL_SCRIPT_FILE_NAME=$(basename -- "$0")
# Shell Script file name after removing extension like "start-yaml-validator"
SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT="${SHELL_SCRIPT_FILE_NAME%.sh}"
# App name after removing start/stop strings like "yaml-validator"
APP_NAME=${SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT#start-}
PIDS=`ps aux |grep [j]ava.*-Dspring.profiles.active=$ACTIVE_PROFILE.*$APP_NAME.*jar | awk {'print $2'}`
if [ -z "$PIDS" ]; then
echo "No instances of $APP_NAME with profile:$ACTIVE_PROFILE is running..." 1>&2
else
for PROCESS_ID in $PIDS; do
echo "Please stop the process($PROCESS_ID) using the shell script: stop-$APP_NAME.sh"
done
exit 1
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 $BASE_PACKAGE/apps/$APP_NAME/$APP_NAME*.jar | head -n1)
# 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 "$APP_NAME start script is completed."
Run Code Online (Sandbox Code Playgroud)
这是停止脚本(stop-appname.sh):
#Active Profile(YAML)
ACTIVE_PROFILE="preprod"
#Base Folder Path like "/folder/packages"
CURRENT_DIR=$(readlink -f "$0")
BASE_PACKAGE="${CURRENT_DIR%/bin/*}"
# Shell Script file name after removing path like "start-yaml-validator.sh"
SHELL_SCRIPT_FILE_NAME=$(basename -- "$0")
# Shell Script file name after removing extension like "start-yaml-validator"
SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT="${SHELL_SCRIPT_FILE_NAME%.*}"
# App name after removing start/stop strings like "yaml-validator"
APP_NAME=${SHELL_SCRIPT_FILE_NAME_WITHOUT_EXT:5}
# Script to stop the application
PID_PATH="$BASE_PACKAGE/config/$APP_NAME/$APP_NAME.pid"
if [ ! -f "$PID_PATH" ]; then
echo "Process Id FilePath($PID_PATH) Not found"
else
PROCESS_ID=`cat $PID_PATH`
if [ ! -e /proc/$PROCESS_ID -a /proc/$PROCESS_ID/exe ]; then
echo "$APP_NAME was not running with PROCESS_ID:$PROCESS_ID.";
else
kill $PROCESS_ID;
echo "Gracefully stopping $APP_NAME with PROCESS_ID:$PROCESS_ID..."
sleep 5s
fi
fi
PIDS=`/bin/ps aux |/bin/grep [j]ava.*-Dspring.profiles.active=$ACTIVE_PROFILE.*$APP_NAME.*jar | /bin/awk {'print $2'}`
if [ -z "$PIDS" ]; then
echo "All instances of $APP_NAME with profile:$ACTIVE_PROFILE has has been successfully stopped now..." 1>&2
else
for PROCESS_ID in $PIDS; do
counter=1
until [ $counter -gt 150 ]
do
if ps -p $PROCESS_ID > /dev/null; then
echo "Waiting for the process($PROCESS_ID) to finish on it's own for $(( 300 - $(( $counter*5)) ))seconds..."
sleep 2s
((counter++))
else
echo "$APP_NAME with PROCESS_ID:$PROCESS_ID is stopped now.."
exit 0;
fi
done
echo "Forcefully Killing $APP_NAME with PROCESS_ID:$PROCESS_ID."
kill -9 $PROCESS_ID
done
fi
Run Code Online (Sandbox Code Playgroud)
使用exit()SpringApplication 类中的静态方法优雅地关闭您的 Spring Boot 应用程序。
public class SomeClass {
@Autowired
private ApplicationContext context
public void close() {
SpringApplication.exit(context);
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
126979 次 |
| 最近记录: |