Linux 如何以正确的方式关闭Spring启动应用程序?
在Spring引导文档中,他们说“每个SpringApplication都将向JVM注册一个关机钩子,以确保ApplicationContext在退出时正常关闭。” 当我在shell命令上单击Linux 如何以正确的方式关闭Spring启动应用程序?,linux,spring,spring-boot,Linux,Spring,Spring Boot,在Spring引导文档中,他们说“每个SpringApplication都将向JVM注册一个关机钩子,以确保ApplicationContext在退出时正常关闭。” 当我在shell命令上单击ctrl+c时,应用程序可以正常关闭。如果在生产机器中运行应用程序,则必须使用以下命令 java-jarproapplicaton.jar。但是我不能关闭shell终端,否则它将关闭进程 如果我运行像nohupjava-jarproapplicaton.jar&这样的命令,我就不能使用ctrl+c优雅地关闭
ctrl+c
时,应用程序可以正常关闭。如果在生产机器中运行应用程序,则必须使用以下命令
java-jarproapplicaton.jar
。但是我不能关闭shell终端,否则它将关闭进程
如果我运行像nohupjava-jarproapplicaton.jar&
这样的命令,我就不能使用ctrl+c
优雅地关闭它
public class SomeClass {
@Autowired
private ApplicationContext context
public void close() {
SpringApplication.exit(context);
}
}
在生产环境中启动和停止Spring Boot应用程序的正确方法是什么?如果您使用的是执行器模块,则可以通过
JMX
或HTTP
关闭应用程序(如果启用了端点)
添加到应用程序属性中
:
endpoints.shutdown.enabled=true
将提供以下URL:
/exactor/shutdown
-允许应用程序正常关闭(默认情况下未启用)
根据端点的公开方式,敏感参数可以用作安全提示
例如,当通过HTTP
访问敏感端点时,它们需要用户名/密码(如果未启用web安全性,则只需禁用)
从中,如果您正在使用maven,则可以使用 (其中嵌入)将输出一个带有start/stop参数的shell脚本。
stop
将正常关闭/终止您的Spring应用程序
new SpringApplicationBuilder(Application.class)
.listeners(new ApplicationErrorListener())
.run(args);
@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");
}
}
}
相同的脚本可用于将maven应用程序用作linux服务。您可以使springboot应用程序将PID写入文件,并且可以使用PID文件停止或重新启动,或者使用bash脚本获取状态。要将PID写入文件,请使用ApplicationPidFileWriter将侦听器注册到SpringApplication,如下所示:
SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./bin/app.pid"));
application.run();
然后编写一个bash脚本来运行spring引导应用程序
现在您可以使用脚本启动、停止或重新启动。至于@Jean-Philippe Bond的答案 下面是一个maven快速示例,maven用户可以使用spring boot starter actuator将HTTP端点配置为关闭spring boot web应用程序,以便您可以复制和粘贴: 1.Maven pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
列出了所有端点:
3.发送post方法以关闭应用程序:
curl -X POST localhost:port/shutdown
安全说明: 如果需要关闭方法auth protected,可能还需要
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
org.springframework.boot
:这里是另一个不需要您更改代码或公开关闭端点的选项。创建以下脚本并使用它们启动和停止应用程序
start.sh
#!/bin/bash
java -jar myapp.jar & echo $! > ./pid.file &
#!/bin/bash
kill $(cat ./pid.file)
#!/bin/bash
nohup ./start.sh > foo.out 2> foo.err < /dev/null &
启动应用程序并将进程id保存在文件中
停止。sh
#!/bin/bash
java -jar myapp.jar & echo $! > ./pid.file &
#!/bin/bash
kill $(cat ./pid.file)
#!/bin/bash
nohup ./start.sh > foo.out 2> foo.err < /dev/null &
使用保存的进程id停止应用程序
启动\u静音。sh
#!/bin/bash
java -jar myapp.jar & echo $! > ./pid.file &
#!/bin/bash
kill $(cat ./pid.file)
#!/bin/bash
nohup ./start.sh > foo.out 2> foo.err < /dev/null &
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);
}
}
}
从Spring Boot 2.3及更高版本开始,有一个内置机制
Pre-Spring Boot 2.3,没有现成的优雅关机机制。
一些spring引导启动器提供以下功能:
我是第一册的作者。起动器被命名为“弹簧启动中断”。它在负载平衡器级别上工作,即简单地将服务标记为OUT OF_service,而不以任何方式干扰应用程序上下文。这允许进行优雅的关机,意味着如果需要,服务可以停止使用一段时间,然后恢复正常。缺点是它不会停止JVM,您必须使用kill
命令来停止JVM。当我在容器中运行所有内容时,这对我来说没什么大不了的,因为无论如何我都必须停止并移除容器
第2号和第3号或多或少是基于安迪·威尔金森的作品。它们是单向工作的——一旦被触发,它们最终会关闭上下文。SpringApplication会向JVM隐式注册一个关闭钩子,以确保ApplicationContext在退出时正常关闭。它还将调用所有用@PreDestroy
注释的bean方法。这意味着我们不必像在spring core应用程序中那样,在启动应用程序中显式使用ConfigurableApplicationContext
的RegisterShotDownhook()
方法
new SpringApplicationBuilder(Application.class)
.listeners(new ApplicationErrorListener())
.run(args);
@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");
}
}
}
所有答案似乎都忽略了一个事实,即在正常关机期间(例如,在企业应用程序中),您可能需要以协调的方式完成部分工作
@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();
}
}
@组件
公共类ApplicationShutton实现ApplicationListener{
@自动连线…//各种组件和服务
@凌驾
Application event(ContextClosedEvent事件)上的公共无效{
service1.changeHeartBeatMessage();//允许负载平衡器和集群为即将到来的关机做好准备
service2.deregisterQueueListeners();
service3.finishProcessingTasksAtHand();
service2.reportFailedTasks();
服务4.优雅地关闭可能已启动的本地系统进程();
service1.EventLogGracefulShutdownPlete();
}
}
如果您在linux环境中,您所要做的就是从/etc/init.d/内部创建指向.jar文件的符号链接
sudo ln -s /path/to/your/myboot-app.jar /etc/init.d/myboot-app
然后,您可以像启动任何其他服务一样启动应用程序
sudo /etc/init.d/myboot-app start
关闭应用程序
sudo /etc/init.d/myboot-app stop
这样,当您退出终端时,应用程序将不会终止。应用程序将使用stop命令正常关闭
server.shutdown.grace-period=30s
@Component
public class AppShutdownHook implements ApplicationListener<ContextClosedEvent> {
private static final Logger logger = LoggerFactory.getLogger(AppShutdownHook.class);
@Override
public void onApplicationEvent(ContextClosedEvent event) {
logger.info("shutdown requested !!!");
try {
//TODO Add logic to shutdown, diff elements of your application
} catch (Exception e) {
logger.error("Exception occcured while shutting down Application:", e);
}
}
}