如何防止Spring Boot守护进程/服务器应用程序立即关闭?

30

我的Spring Boot应用不是Web服务器,但它是一个使用自定义协议的服务器(在这种情况下使用Camel)。

但是Spring Boot启动后立即停止(优雅地)。我该如何防止这种情况发生?

我希望在按下Ctrl+C或以编程方式停止应用时才停止。

@CompileStatic
@Configuration
class CamelConfig {

    @Bean
    CamelContextFactoryBean camelContext() {
        final camelContextFactory = new CamelContextFactoryBean()
        camelContextFactory.id = 'camelContext'
        camelContextFactory
    }

}

你如何暴露你的端点,能分享一些代码吗? - Haim Raman
@Haim,它在https://github.com/lumenitb/lumen/tree/master/social中。 - Hendy Irawan
9个回答

38

我找到了解决方案,使用org.springframework.boot.CommandLineRunner + Thread.currentThread().join(),例如: (注意:下面的代码是 Groovy,而不是 Java)

package id.ac.itb.lumen.social

import org.slf4j.LoggerFactory
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
class LumenSocialApplication implements CommandLineRunner {

    private static final log = LoggerFactory.getLogger(LumenSocialApplication.class)

    static void main(String[] args) {
        SpringApplication.run LumenSocialApplication, args
    }

    @Override
    void run(String... args) throws Exception {
        log.info('Joining thread, you can press Ctrl+C to shutdown application')
        Thread.currentThread().join()
    }
}

你的主方法是什么?那个不会编译。 - Stealth Rabbi
@stealth Rabbi 静态无返回值主函数(String[] 参数) - Hendy Irawan
好的,但是看一下main()函数。它似乎缺少一些括号。这段代码无法编译。"SpringApplication.run LumenSocialApplication, args" - Stealth Rabbi
@Stealth Rabbi 我要提一下,我的代码是Groovy,不是Java。 - Hendy Irawan
5
这将破坏使用SpringBootTest注解的集成测试。 - christopher
1
只有在您的应用程序正常运行时才能起作用。如果它崩溃了,它将挂起而不是退出,这是一个问题。 - Renann

23
自Apache Camel 2.17版本以来,有一个更简洁的解决方案。引用http://camel.apache.org/spring-boot.html的说法:

为了让主线程保持阻塞状态,使Camel保持运行,请在应用程序属性或应用程序.yml文件中包含spring-boot-starter-web依赖项,或添加camel.springboot.main-run-controller=true。

您还需要以下依赖项:

<dependency> <groupId>org.apache.camel</groupId> <artifactId>camel-spring-boot-starter</artifactId> <version>2.17.0</version> </dependency>

请明确替换<version>2.17.0</version>或使用camel BOM导入依赖项管理信息以保持一致性。

15

使用CountDownLatch的示例实现:

@Bean
public CountDownLatch closeLatch() {
    return new CountDownLatch(1);
}

public static void main(String... args) throws InterruptedException {
    ApplicationContext ctx = SpringApplication.run(MyApp.class, args);  

    final CountDownLatch closeLatch = ctx.getBean(CountDownLatch.class);
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            closeLatch.countDown();
        }
    });
    closeLatch.await();
}

现在要停止您的应用程序,您可以查找进程 ID 并从控制台发出 kill 命令:

现在,您可以通过查找进程 ID 并在控制台中使用 kill 命令来停止应用程序:

返回结果:

现在,您可以通过查找进程 ID 并在控制台中使用 kill 命令来停止应用程序:

kill <PID>

5
Spring Boot将运行应用程序的任务留给实施应用程序的协议。例如,请参见此 指南:

还需要一些像 CountDownLatch 这样的清理对象来保持主线程活动...

因此,例如,运行Camel服务的方法就是从您的主Spring Boot应用程序类中作为独立应用程序 运行Camel。

2
这个指南的链接已经失效。 - Kris
看起来Spring更新了他们的指南,原始文章不再可用。 - Anatoly

4

所有线程都已完成,程序将自动关闭。 因此,在@Scheduled中注册一个空任务将创建一个循环线程以防止关闭。

文件 application.yml

spring:
  main:
    web-application-type: none

文件 DemoApplication.java

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

   public static void main(String[] args) {
       SpringApplication.run(DemoApplication.class, args);
   }

}

文件 KeepAlive.java

@Component
public class KeepAlive {

   private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
   private static final SimpleDateFormat dateFormat =  new SimpleDateFormat("HH:mm:ss");

   @Scheduled(fixedRate = 1 * 1000 * 60) // 1 minute
   public void reportCurrentTime() {
       log.info("Keepalive at time {}", dateFormat.format(new Date()));
   }
}

这可以是一个回答而不是评论。请阅读规则。 - dpapadopoulos

4

现在这变得更加简单了。

只需在您的 application.properties 文件中添加 camel.springboot.main-run-controller=true


0

我的项目是非Web Spirng Boot。 我的优雅解决方案是通过CommandLineRunner创建一个守护线程。 这样,应用程序不会立即关闭。

 @Bean
    public CommandLineRunner deQueue() {
        return args -> {
            Thread daemonThread;
            consumer.connect(3);
            daemonThread = new Thread(() -> {
                try {
                    consumer.work();
                } catch (InterruptedException e) {
                    logger.info("daemon thread is interrupted", e);
                }
            });
            daemonThread.setDaemon(true);
            daemonThread.start();
        };
    }

-3

如果不部署Web应用程序,要保持Java进程的活动状态,请将webEnvironment属性设置为false,如下所示:

 SpringApplication sa = new SpringApplication();
 sa.setWebEnvironment(false); //important
 ApplicationContext ctx = sa.run(ApplicationMain.class, args);

-5

为了让SpringBoot应用程序持续运行,它必须在容器中运行,否则它就像任何Java应用程序一样,所有线程都完成并结束。

您可以添加

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

如果它可以转换成Web应用程序,那么它会自动完成,否则您需要在实现中保持其运行。


就像我之前所说,它不是一个Web服务器。那么我的问题是...我该如何模拟Tomcat的行为?我要阻塞主线程还是类似的操作?有什么“官方”的方法吗? - Hendy Irawan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接