Eclipse 中终止的 Spring Boot 应用程序 - 未调用关闭挂钩

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39193836/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 23:16:28  来源:igfitidea点击:

Terminated Spring Boot App in Eclipse - Shutdown hook not called

javaeclipsespringspring-bootredis

提问by alexbt

I have a Spring Boot + Spring Data Redis/KeyValue project. I setup a Spring profile to run the application with all dependencies embedded. So at startup, I launch an embedded Redis Server. Everything works fine when I start it in Eclipse, except that I would like the Redis server to be stopped when I stop the Spring Boot application. So I setup several shutdown hooks, however they are not called when I terminate the application from Eclipse.

我有一个 Spring Boot + Spring Data Redis/KeyValue 项目。我设置了一个 Spring 配置文件来运行嵌入了所有依赖项的应用程序。所以在启动时,我启动了一个嵌入式 Redis 服务器。当我在 Eclipse 中启动它时,一切正常,除了我希望在停止 Spring Boot 应用程序时停止 Redis 服务器。所以我设置了几个关闭挂钩,但是当我从 Eclipse 终止应用程序时它们不会被调用。

They are similar questions on SO, I created this one hoping there would be a Redis solution. Also none of these similar questions are specific to Spring Boot.

他们是关于 SO 的类似问题,我创建了这个,希望有一个 Redis 解决方案。同样,这些类似的问题都不是 Spring Boot 特有的。

I tried many things:

我尝试了很多事情:

  • Spring Boot's ExitCodeGenerator;
  • DisposableBean;
  • @PreDestroy;
  • I tried a ShutdownHook (after @mp911de's answer)
  • Spring Boot 的ExitCodeGenerator
  • DisposableBean;
  • @PreDestroy;
  • 我尝试了 ShutdownHook(在@mp911de 的回答之后)

None of them are called.

他们都没有被调用。

Perhaps there is a Redis option, timeout, keep alive.. something outside the box I am not aware of ?How can I ensure that the Redis Server is stopped when my Spring Boot app is abruptly stopped?

也许有一个 Redis 选项,超时,保持活动......一些我不知道的盒子外的东西?当我的 Spring Boot 应用程序突然停止时,如何确保 Redis 服务器停止

=> I saw this Embedded Redis for spring boot, but @PreDestroyis just not called when killing the application unexpectedly.

=> 我看到了这个嵌入式 Redis for spring boot,但是@PreDestroy在意外杀死应用程序时没有调用。

Here are some of the similar questions:

以下是一些类似的问题:

I also saw this post on eclipse.org discussing how shutdown hook is not called when stopping an application from eclipse: Graceful shutdown of Java Applications

我还在 eclipse.org 上看到了这篇文章,讨论了从 Eclipse 停止应用程序时如何不调用关闭钩子:Java 应用程序的优雅关闭



Here's all my relevant code:

这是我所有的相关代码:

Component to start the embeddedRedis server at startup (With my attempts to stop it too!!):

在启动时启动嵌入式Redis 服务器的组件(我也尝试停止它!!):

@Component
public class EmbeddedRedis implements ExitCodeGenerator, DisposableBean{

    @Value("${spring.redis.port}")
    private int redisPort;

    private RedisServer redisServer;

    @PostConstruct
    public void startRedis() throws IOException {
        redisServer = new RedisServer(redisPort);
        redisServer.stop();
        redisServer.start();
    }

    @PreDestroy
    public void stopRedis() {
        redisServer.stop();
    }

    @Override
    public int getExitCode() {
        redisServer.stop();
        return 0;
    }

    @Override
    public void destroy() throws Exception {
        redisServer.stop();
    }
}

application.properties:

应用程序属性:

spring.redis.port=6379

Spring Boot App:

春季启动应用程序:

@SpringBootApplication
@EnableRedisRepositories
public class Launcher {

    public static void main(String[] args){
        new SpringApplicationBuilder() //
        .sources(Launcher.class)//
        .run(args);
    }

    @Bean
    public RedisTemplate<String, Model> redisTemplate() {
        RedisTemplate<String, Model> redisTemplate = new RedisTemplate<String, Model>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        return redisTemplate;
    }


    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName("localhost");
        return jedisConnectionFactory;
    }
}

Redis server (~ embedded) running:

Redis 服务器(~嵌入式)正在运行:

$ ps -aef | grep redis
 ... /var/folders/qg/../T/1472402658070-0/redis-server-2.8.19.app *:6379  

The embedded Redis maven dependency:

嵌入式 Redis Maven 依赖项:

<dependency>
    <groupId>com.github.kstyrc</groupId>
    <artifactId>embedded-redis</artifactId>
    <version>0.6</version>
</dependency>

回答by Ghurdyl

When using SpringBoot and Eclipse, you can install the STS(Spring Tool Suite) eclipse plugin to achieve graceful shutdown.

使用SpringBoot和Eclipse时,可以安装STS(Spring Tool Suite)eclipse插件实现优雅关机。

Once installed, run the application as a "Spring Boot App" instead of regular "Java application" (Run/debug configurations)

安装后,将应用程序作为“Spring Boot App”而不是常规的“Java 应用程序”运行(运行/调试配置)

Ensure that the checkbox "Enable Life Cycle Management" checkbox is checked and when you will click on the red square button to stop the application, it will perform a graceful shutdown instead of a hard kill.

确保选中“启用生命周期管理”复选框,当您单击红色方形按钮停止应用程序时,它将执行正常关闭而不是硬终止。

Edit:

编辑:

It's worth noticing that there are two "red square button". One in the "Launch" toolbar and one in the "Console" panel. The one in the launch toolbar still performs hard kill but the one in the console allows the graceful shutdown for spring-boot application (launched with STS)

值得注意的是,有两个“红色方形按钮”。一个在“启动”工具栏中,一个在“控制台”面板中。启动工具栏中的一个仍然执行硬终止,但控制台中的一个允许 spring-boot 应用程序正常关闭(使用 STS 启动)

回答by alexbt

After investigation, it turns out that Eclipsesimply terminates the application without any chance for a shutdown hook to be run.

经过调查,结果证明Eclipse只是终止了应用程序,而没有任何机会运行关闭挂钩。

I've found a workaround/hack thanks to how Spring-Bootis wired. When the main method is executed, tomcat is started in another thread and the main method continues to execute until completion. This allowed me to insert a termination logicwhen you hit 'Enter'.

由于Spring-Boot接线方式,我找到了一种解决方法/hack 。当main方法被执行时,tomcat在另一个线程中启动,main方法继续执行,直到完成。这允许我在您点击“Enter”时插入终止逻辑

The application launches normally and the console simply awaits for an enter key to execute System.exit(1);:

应用程序正常启动,控制台只是等待输入键执行System.exit(1);

@SpringBootApplication
@EnableRedisRepositories
public class Launcher {
    public static void main(String[] args){
        new SpringApplicationBuilder() //
        .sources(Launcher.class)//
        .run(args);

        System.out.println("Press 'Enter' to terminate");
        new Scanner(System.in).nextLine();
        System.out.println("Exiting");
        System.exit(1);
    }
}

When launching the application from Eclipse, instead of terminating the application from the interface, I now hit enter in the console. Now, the shutdown hooks (@PreDestroy) is triggered and the Redis server is stopped!

从 Eclipse 启动应用程序时,我现在不是从界面终止应用程序,而是在控制台中按 Enter。现在,关闭钩子 ( @PreDestroy) 被触发并停止 Redis 服务器!

Not what I hoped for, but at least for the time being the Embedded Redis Server is stopped with the application and I don't have to keep killing it manually.

不是我所希望的,但至少目前嵌入式 Redis 服务器已被应用程序停止,我不必继续手动杀死它。

回答by mp911de

ExitCodeGeneratorrequires the application to call the exitmethod

ExitCodeGenerator要求应用程序调用该exit方法

System.exit(SpringApplication
             .exit(SpringApplication.run(SampleBatchApplication.class, args)));

You could register additionally a shutdown hook. Hooks will be called

您可以另外注册一个关闭钩子。钩子会被调用

  • when the VM is terminated normally (System.exit).
  • in response to an interrupt (Ctrl+C, SIGINT) or signal (SIGHUP, SIGTERM).
  • 当 VM 正常终止时 ( System.exit)。
  • 响应中断 ( Ctrl+C, SIGINT) 或信号 ( SIGHUP, SIGTERM)。

In some circumstances, if the VM does not shut down cleanly (SIGKILL, internal VM errors, errors in native methods), there's no guarantee whether shutdown hooks are called or not.

在某些情况下,如果 VM 没有完全关闭(SIGKILL内部 VM 错误、本机方法中的错误),则无法保证是否调用了关闭挂钩。

The code for your EmbeddedRediscomponent could look like:

您的EmbeddedRedis组件的代码可能如下所示:

@PostConstruct
public void startRedis() throws IOException {

    redisServer = new RedisServer(redisPort);
    redisServer.start();

    Runtime.getRuntime().addShutdownHook(new Thread(){

        @Override
        public void run() {
            redisServer.stop();
        }
    });
}

回答by Ben Ingle

I had the same problem, though not with Redis. I wanted maximum portability so other developers don't have to add STS if they don't want to. You can add this to any Spring Boot application to offer clean shutdown. Here's what I did, elaborating on the OP's own answer.

我遇到了同样的问题,尽管 Redis 没有。我想要最大的可移植性,所以其他开发人员如果不想的话就不必添加 STS。您可以将此添加到任何 Spring Boot 应用程序以提供干净的关闭。这就是我所做的,详细说明了 OP 自己的答案。

Add this to your main class, or any @Configurationclass:

将此添加到您的主类或任何@Configuration类:

    @Bean
    public ApplicationRunner systemExitListener() {
        return args -> {
            if (args.getOptionValues("exitListener") != null) {
                System.out.println("Press Enter to exit application");
                new Scanner(System.in).nextLine();
                System.out.println("Exiting");
                System.exit(0);
            }
        };
    }

Then in Eclipse (or in your IDE of choice, or even on the command line), add the argument --exitListenerto activate the code. In Eclipse, that would be in Run Configurations, on the Arguments tab, in the Program Arguments box.

然后在 Eclipse(或在您选择的 IDE 中,甚至在命令行中)中,添加参数--exitListener以激活代码。在 Eclipse 中,它位于 Run Configurations 中的 Arguments 选项卡上的 Program Arguments 框中。