【问题标题】:Terminated Spring Boot App in Eclipse - Shutdown hook not called在 Eclipse 中终止 Spring Boot 应用程序 - 未调用关闭挂钩
【发布时间】:2017-01-04 18:02:51
【问题描述】:

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

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

我尝试了很多东西:

  • Spring Boot 的ExitCodeGenerator
  • DisposableBean;
  • @PreDestroy;
  • 我尝试了一个 ShutdownHook(在@mp911de 的回答之后)

没有一个被调用。

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

=> 我看到了这个Embedded Redis for spring boot,但是当意外终止应用程序时,@PreDestroy 只是没有被调用。

以下是一些类似的问题:

我还在 eclipse.org 上看到了这篇文章,讨论了从 eclipse 停止应用程序时如何不调用关闭挂钩:Graceful shutdown of Java Applications


这是我所有的相关代码:

在启动时启动嵌入式 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 应用:

@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 服务器(~嵌入式)正在运行:

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

内嵌的 Redis maven 依赖:

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

【问题讨论】:

    标签: java eclipse spring spring-boot redis


    【解决方案1】:

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

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

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

    编辑:

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

    【讨论】:

    • 从 STS 4.5.1 开始,启动工具栏中的“红色方形按钮”似乎也执行了正常关闭,以及“重新启动”按钮:在这两种情况下,我都得到了“应用程序关闭请求”日志输出,执行硬杀时不显示。相反,我现在找不到从 IDE 进行硬杀的方法,有时它仍然有用。
    【解决方案2】:

    经过调查,发现Eclipse 只是简单地终止了应用程序,没有任何机会运行关闭挂钩。

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

    应用程序正常启动,控制台只是等待输入键执行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);
        }
    }
    

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

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

    【讨论】:

    • 您也可以将此代码分解为 CommandLineRunner 或 ApplicationRunner bean @Bean public ApplicationRunner systemExitListener() { return args -> { System.out.println("Press Enter to exit application");新扫描仪(System.in).nextLine(); System.out.println("退出"); System.exit(1); }; }
    【解决方案3】:

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

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

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

    • VM 正常终止时 (System.exit)。
    • 响应中断(Ctrl+CSIGINT)或信号(SIGHUPSIGTERM)。

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

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

    @PostConstruct
    public void startRedis() throws IOException {
    
        redisServer = new RedisServer(redisPort);
        redisServer.start();
    
        Runtime.getRuntime().addShutdownHook(new Thread(){
    
            @Override
            public void run() {
                redisServer.stop();
            }
        });
    }
    

    【讨论】:

    • Spring 也使用关闭挂钩来保证上下文关闭,但令我困惑的是 @PreDestroy 未被调用。
    • 我刚刚意识到我混合了两种行为,一种来自命令行和来自 IDE。我真正面临的问题来自IDE。我会更新问题。 PreDestroy 和 DisposableBean 的销毁从命令行工作。
    • 我更新了这个问题,很抱歉误导了你,你确实让我意识到我必须为 ExitCodeGenerator 调用 System.exit :) 我 +1 你的答案。
    【解决方案4】:

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

    将此添加到您的主类或任何@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);
                }
            };
        }
    

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

    【讨论】:

      【解决方案5】:

      要解决在停止 maven 构建并启用调试时杀死 Spring Boot 应用程序的问题: Maven 构建 -> JRE -> VM 参数
      添加-Dspring-boot.run.fork=false

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-23
        • 2018-09-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-05
        相关资源
        最近更新 更多