我有一个spring-boot应用程序。
我在我的bean中实现了SmartLifecycle
接口,它在start
方法中启动async snmp服务器,并以stop
方法停止它。
一切正常,除了主应用程序上下文在启动后立即停止的事实,所以我的服务器bean也在启动后立即停止。
我只需要在关闭挂钩时停止弹簧上下文。
这不是一个Web应用程序,因此我不需要spring-boot-starter-web
,这可以通过启动webserver来解决此问题,该服务器会阻止上下文停止,直到Web服务器停止。
我可以使用类似CountDownLatch
之类的内容,并在上下文启动后立即在main
方法中等待它为零。 Somethig喜欢这样:
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext ctx = SpringApplication.run(SnmpTrapRetranslatorApplication.class, args);
CountDownLatch snmpServerCloseLatch = ctx.getBean("snmpServerCloseLatch", CountDownLatch.class);
snmpServerCloseLatch.await();
}
我的服务器bean的start
方法将使用count 1
创建此锁存器,而stop
方法将调用snmpServerCloseLatch.countDown()
。
此技术描述为here。
但是错误的是我的main
方法负责等待我的自定义服务器bean停止。我觉得这不对。
例如spring-boot-starter-web
如何做到这一点?当它启动tomcat时,它会一直运行,直到收到shutdown hook并且不需要main
方法中的任何管理代码。它仅在上下文接收到shoutdown信号时停止。
例如,当我在bean中使用@Scheduled
方法时,同样的行为。 Spring也不会自动停止上下文。仅限于CTRL-C
。
我想达到类似的效果。我的main
方法应该只有一行:启动上下文。上下文应该在启动或停止时启动和停止异步服务器(已由SmartLifecycle
实现),并且在请求关闭之前不应该停止(CTRL-C,SIGINT等)。
答案 0 :(得分:3)
我的调查引导我解决问题的核心:daemon threads。
我使用的snmp服务器实现(snmp4j)在内部使用守护程序线程。因此,即使snmp服务器启动,JVM中也没有更多的实时用户线程,因此它会退出。
<强> TL / DR 强>
只需将此方法添加到任何bean(snmp服务器bean是这个的好选择):
@Scheduled(fixedDelay = 1000 * 60 * 60) // every hour
public void doNothing() {
// Forces Spring Scheduling managing thread to start
}
(不要忘记在弹簧配置中添加@EnableScheduling
)。
<强>解释强>
为防止停止弹簧上下文,当SNMP服务器仍在运行时,我们需要任何非守护程序线程在JVM中处于活动状态。不一定是main
线程。所以我们可以让main
方法完成。
我们可以从我们的服务器bean的start
方法运行新的非守护程序线程。该帖子会wait
对某些while
变量进行running
循环检查,而我们的stop
方法会将此running
变量设置为false
这个锁上的{和} notifyAll
。
这样,我们的非守护程序线程将一直处于活动状态,直到触发了击倒钩子(并阻止JVM退出)。
在关闭钩子之后,spring上下文生命周期close
方法将调用所有SmartLifecycle
bean的close
方法,这将导致SNMP服务器bean的stop
方法调用,这将导致将running
设置为false,这将导致我们的非守护程序线程停止,从而允许JVM正常停止。
或者我们可以以类似的方式使用Spring的调度线程。它也是非守护程序线程,因此它将阻止JVM退出。 Spring自己管理这个线程,所以当触发关闭钩子时它会自动停止它。
要使Spring的调度线程启动,我们需要在任何bean中使用任何@Scheduled
方法。
我认为第一种(手动)方法仍然更多&#34;正确&#34;,同时需要更多异步编码(这是我们都知道的容易出错)。谁知道Spring将如何改变它未来的日程安排实施。
答案 1 :(得分:1)
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();
}
}));