How to prevent Spring app context shutdown until s

2019-07-16 04:51发布

I have a spring-boot application.

I have implemented SmartLifecycle interface in my bean which starts async snmp server in it's start method and stops it in it's stop method.

All working fine, except the fact that main application context stops right after start, so my server bean also stops right after start.

All I need is to make spring context to stop only when shutdown hook is fired.

This is not a web application, so I don't need spring-boot-starter-web, which is solves this problem by starting webserver which prevents context stop until webserver stops.

I can use something like CountDownLatch and waiting for it to be zero in my main method right after context starts. Somethig like this:

public static void main(String[] args) throws InterruptedException {
    ConfigurableApplicationContext ctx = SpringApplication.run(SnmpTrapRetranslatorApplication.class, args);
    CountDownLatch snmpServerCloseLatch = ctx.getBean("snmpServerCloseLatch", CountDownLatch.class);
    snmpServerCloseLatch.await();
}

And my server bean's start method will create this latch with count 1, while stop method will call snmpServerCloseLatch.countDown().

This technique is described here.

But what wrong with this is that my main method is responsible for waiting my custom server bean to stop. I feel this just not right.

How for example spring-boot-starter-web do this? When it starts tomcat, it keeps running until shutdown hook is received and it don't need to have any managing code in the main method. It stops only when context receiving shoutdown signal.

The same behaviour is for example when I have @Scheduled method in my bean. Spring also doesn't stops context automatically. Only on CTRL-C.

I want to achieve similar effect. My main method should have only one line: start the context. Context should start and stop my async server when it starts or stops (already achieved by SmartLifecycle) and should not stop until shutdown is requested (CTRL-C, SIGINT etc).

2条回答
成全新的幸福
2楼-- · 2019-07-16 05:09
    SpringApplication app = new SpringApplication(Main.class);
    app.setRegisterShutdownHook(false);
    ConfigurableApplicationContext applicationContext= app.run();
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        @Override
        public void run() {
            //do your things
            applicationContext.close();
        }
    }));
查看更多
劳资没心,怎么记你
3楼-- · 2019-07-16 05:15

My investigation lead me to the core of the problem: daemon threads.

The snmp server implementation which I use (snmp4j) use daemon threads internally. So even when snmp server started, there are no more live user threads in JVM, so it exits.

TL/DR:

Just add this method to any bean (snmp server bean is good candidate for this):

@Scheduled(fixedDelay = 1000 * 60 * 60) // every hour
public void doNothing() {
    // Forces Spring Scheduling managing thread to start
}

(Do not forget to add @EnableScheduling to your spring configuration).

Explanation:

To prevent stopping spring context, while SNMP server is still running, we need any non-daemon thread to be alive in JVM. Not necessarily main thread. So we can let main method to finish.

  1. We can run new non-daemon thread from our server bean's start method. This thread will wait on some lock in while loop checking for some running variable, while our stop method will set this running variable to false and notifyAll on this lock.

    This way, our non-daemon thread will be alive until shotdown hook is triggered (and prevents JVM to exit). After shutdown hook, spring context lifecycle close method will call all SmartLifecycle bean's close methods, that will lead to SNMP server bean's stop method call, that will lead to set running to false, that will lead to our non-daemon thread to stop, that allow JVM to stop gracefully.

  2. Or instead we can use Spring's scheduling thread in similar way. It also is non-daemon thread, so it will prevent JVM to exit. And Spring manages this thread itself, so it will automatically stop it when shutdown hook is triggered.

    To make Spring's scheduling thread to start we need any @Scheduled method in any bean.


I think that first (manual) approach is still more "correct", while requires more async coding (which is error-prone as we all know). Who knows how Spring will change it's scheduling implementation in the future.

查看更多
登录 后发表回答