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方法会调用所有SmartLifecyclebean的close方法,导致SNMP服务器bean的stop方法调用,导致设置running为false,导致我们的非守护线程停止,让JVM优雅停止.
或者,我们可以以类似的方式使用 Spring 的调度线程。它也是非守护线程,因此会阻止JVM退出。而Spring自己管理这个线程,所以在触发shutdown hook时会自动停止。
要启动 Spring 的调度线程,我们需要@Scheduled任何 bean 中的任何方法。
我认为第一种(手动)方法仍然更“正确”,同时需要更多的异步编码(众所周知,这很容易出错)。谁知道 Spring 将来会如何改变它的调度实现。
| 归档时间: |
|
| 查看次数: |
7244 次 |
| 最近记录: |