Rus*_*nko 3 java spring asynchronous spring-boot
我有一个 spring-boot 应用程序。
我已经SmartLifecycle
在我的 bean 中实现了接口,start
它在它的stop
方法中启动异步 snmp 服务器并在它的方法中停止它。
一切正常,除了主应用程序上下文在启动后立即停止,所以我的服务器 bean 也在启动后立即停止。
我所需要的只是让 spring 上下文仅在关闭挂钩被触发时停止。
这不是一个网络应用程序,所以我不需要spring-boot-starter-web
,这是通过启动 webserver 来解决这个问题的,它可以防止上下文停止,直到 webserver 停止。
我可以在上下文开始后CountDownLatch
立即使用类似的东西并等待它在我的main
方法中为零。像这样:
public static void main(String[] args) throws InterruptedException {
ConfigurableApplicationContext ctx = SpringApplication.run(SnmpTrapRetranslatorApplication.class, args);
CountDownLatch snmpServerCloseLatch = ctx.getBean("snmpServerCloseLatch", CountDownLatch.class);
snmpServerCloseLatch.await();
}
Run Code Online (Sandbox Code Playgroud)
我的服务器 bean 的start
方法将使用 count 创建这个闩锁1
,而stop
方法将调用snmpServerCloseLatch.countDown()
.
但是这有什么问题,我的main
方法负责等待我的自定义服务器 bean 停止。我觉得这不太对。
例如如何spring-boot-starter-web
做到这一点?当它启动 tomcat 时,它会一直运行直到收到关闭钩子,并且它不需要在main
方法中有任何管理代码。只有当上下文接收到shoudown 信号时它才会停止。
例如,当@Scheduled
我的 bean 中有方法时,也会出现相同的行为。Spring 也不会自动停止上下文。仅在CTRL-C
.
我想达到类似的效果。我的main
方法应该只有一行:启动上下文。上下文应在启动或停止时启动和停止我的异步服务器(已通过 实现SmartLifecycle
),并且在请求关闭之前不应停止(CTRL-C、SIGINT 等)。
小智 6
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();
}
}));
Run Code Online (Sandbox Code Playgroud)
我的调查使我找到了问题的核心:守护线程。
我使用的 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
}
Run Code Online (Sandbox Code Playgroud)
(不要忘记添加@EnableScheduling
到您的弹簧配置中)。
解释:
为了防止停止 spring 上下文,当 SNMP 服务器仍在运行时,我们需要任何非守护线程在 JVM 中处于活动状态。不一定是main
线程。所以我们可以让main
方法完成。
我们可以从我们的服务器 bean 的start
方法运行新的非守护线程。该线程将wait
在一些锁定while
回路检测一些running
变量,而我们的stop
方法将它设置running
变量false
,并notifyAll
在此锁定。
这样,我们的非守护进程线程将一直处于活动状态,直到触发shotdown 挂钩(并阻止JVM 退出)。关闭钩子后,spring上下文生命周期close
方法会调用所有SmartLifecycle
bean的close
方法,导致SNMP服务器bean的stop
方法调用,导致设置running
为false,导致我们的非守护线程停止,让JVM优雅停止.
或者,我们可以以类似的方式使用 Spring 的调度线程。它也是非守护线程,因此会阻止JVM退出。而Spring自己管理这个线程,所以在触发shutdown hook时会自动停止。
要启动 Spring 的调度线程,我们需要@Scheduled
任何 bean 中的任何方法。
我认为第一种(手动)方法仍然更“正确”,同时需要更多的异步编码(众所周知,这很容易出错)。谁知道 Spring 将来会如何改变它的调度实现。