Spring Boot 2.5.1 服务不会在 System.exit(0) 停止
Springboot 2.5.1 service doesn't stop on System.exit(0)
我们有一个 spring-boot 服务,它需要在使用 flyway 进行一些数据库迁移后停止。我们通过回调和行
解决了这个问题
int exitCode = SpringApplication.exit(applicationContext, () -> 0);
System.exit(exitCode);
到目前为止它运行良好 (spring-boot 2.4.x),但最近,我们升级到 2.5.1,现在它不再停止了。这是错误还是功能?现在还有其他方法可以关闭服务吗?
编辑
我做了一个线程转储,但我不太确定如何解释它
"main" #1 prio=5 os_prio=0 tid=0x00000000027eb000 nid=0x1280 in Object.wait() [0x000000000276d000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000006c29756c8> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1252)
- locked <0x00000006c29756c8> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1326)
at java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:107)
at java.lang.ApplicationShutdownHooks.run(ApplicationShutdownHooks.java:46)
at java.lang.Shutdown.runHooks(Shutdown.java:123)
at java.lang.Shutdown.sequence(Shutdown.java:170)
at java.lang.Shutdown.exit(Shutdown.java:216)
- locked <0x00000006c28bc680> (a java.lang.Class for java.lang.Shutdown)
at java.lang.Runtime.exit(Runtime.java:109)
at java.lang.System.exit(System.java:973)
at com.whatsoever.glp.moneypenny.config.PostMigrationCallback.supports(PostMigrationCallback.java:44)
at org.flywaydb.core.internal.callback.DefaultCallbackExecutor.execute(DefaultCallbackExecutor.java:115)
at org.flywaydb.core.internal.callback.DefaultCallbackExecutor.onMigrateOrUndoEvent(DefaultCallbackExecutor.java:73)
at org.flywaydb.core.internal.command.DbMigrate.migrate(DbMigrate.java:165)
at org.flywaydb.core.Flyway.execute(Flyway.java:216)
at org.flywaydb.core.Flyway.execute(Flyway.java:165)
at org.flywaydb.core.Flyway.execute(Flyway.java:572)
at org.flywaydb.core.Flyway.migrate(Flyway.java:165)
at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:66)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=12=](AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda7/370475881.getObject(Unknown Source)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
- locked <0x00000006c295db30> (a java.util.concurrent.ConcurrentHashMap)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
- locked <0x00000006c2636ae8> (a java.lang.Object)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332)
at com.whatsoever.glp.moneypenny.Application.main(Application.java:28)
"RMI TCP Accept-0" #13 daemon prio=5 os_prio=0 tid=0x000000001f040800 nid=0x1554 runnable [0x000000001fcff000]
java.lang.Thread.State: RUNNABLE
at java.net.DualStackPlainSocketImpl.accept0(Native Method)
at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
- locked <0x00000006c261d120> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:560)
at java.net.ServerSocket.accept(ServerSocket.java:528)
at sun.management.jmxremote.LocalRMIServerSocketFactory.accept(LocalRMIServerSocketFactory.java:52)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377)
at java.lang.Thread.run(Thread.java:748)
"SpringApplicationShutdownHook" #14 prio=5 os_prio=0 tid=0x0000000021126000 nid=0x630 waiting for monitor entry [0x0000000022e3f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1021)
- waiting to lock <0x00000006c2636ae8> (a java.lang.Object)
at org.springframework.boot.SpringApplicationShutdownHook.closeAndWait(SpringApplicationShutdownHook.java:128)
at org.springframework.boot.SpringApplicationShutdownHook$$Lambda1/1430717967.accept(Unknown Source)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationShutdownHook.run(SpringApplicationShutdownHook.java:101)
at java.lang.Thread.run(Thread.java:748)
"RMI TCP Connection(1)-172.19.5.119" #15 daemon prio=5 os_prio=0 tid=0x00000000202bf000 nid=0x594 runnable [0x000000002163e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000006c26360a8> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run[=12=](TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda8/349364571.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001ec8f800 nid=0x1164 runnable [0x000000001f3be000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x00000006c2641f80> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x00000006c2641f80> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2.run(AppMainV2.java:48)
"RMI Scheduler(0)" #16 daemon prio=5 os_prio=0 tid=0x00000000202e0000 nid=0x1ac4 waiting on condition [0x000000002173e000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006c261cc28> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001d255000 nid=0x2370 in Object.wait() [0x000000001e78f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000006c2643898> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000006c2643898> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001d254000 nid=0x2ae8 in Object.wait() [0x000000001e68f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000006c261d9d8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000006c261d9d8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001ecf3000 nid=0x28a4 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001ece4800 nid=0x24e4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001ed29000 nid=0x27dc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001ed28000 nid=0x3170 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001ed1f800 nid=0x4108 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001d263000 nid=0x2980 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001d262000 nid=0x2144 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"VM Thread" os_prio=2 tid=0x000000001d250800 nid=0x4568 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000000000283e000 nid=0x1254 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000283f800 nid=0x4490 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002841000 nid=0x28b0 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002842800 nid=0x325c runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002846000 nid=0x1a18 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002847000 nid=0x35c0 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000000000284a800 nid=0xdfc runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000000000284b800 nid=0x2fc0 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001f04c000 nid=0x492c waiting on condition
JNI global references: 1665
我猜是关于这个 等待锁定 <0x00000006c28ae9a0>,但我不确定
编辑 2
这里是相关的类涉及
迁移完成后用于关闭服务的回调
@Component
public class PostMigrationCallback implements Callback {
private final FlywayCustomProperties flywayCustomProperties;
private final ApplicationContext applicationContext;
public PostMigrationCallback(FlywayCustomProperties flywayCustomProperties, ApplicationContext applicationContext) {
this.flywayCustomProperties = flywayCustomProperties;
this.applicationContext = applicationContext;
}
@Override
public boolean supports(Event event, Context context) {
if (event.getId().equalsIgnoreCase("afterMigrate") && flywayCustomProperties.isMigrationOnly()) {
log.info("Service is going to shutdown as configuration was set to spring.flyway.migrate-only=true and schema was migrated successfully");
int exitCode = SpringApplication.exit(applicationContext, () -> 0);
System.exit(exitCode);
}
return false;
}
@Override
public boolean canHandleInTransaction(Event event, Context context) {
return false;
}
@Override
public void handle(Event event, Context context) {
}
@Override
public String getCallbackName() {
return "MigrationOnlyCallback";
}
}
工厂配置回调
@Configuration
@ConditionalOnProperty(
value = "spring.flyway.enabled",
havingValue = "true"
)
public class FlywayFactory {
private PostMigrationCallback postMigrationCallback;
public FlywayFactory(PostMigrationCallback postMigrationCallback) {
this.postMigrationCallback = postMigrationCallback;
}
@Bean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
Flyway flywayWithCallback = Flyway.configure().configuration(flyway.getConfiguration()).callbacks(flywayCallback()).load();
return new FlywayMigrationInitializer(flywayWithCallback);
}
@Bean
public Callback flywayCallback() {
return postMigrationCallback;
}
}
感谢线程转储。问题是由 Spring 中的 a possible bug 引起的 Spring Boot 在刷新应用程序上下文时调用 System.exit(int)
时死锁。感谢您提请我们注意。
在 Spring 引导中解决问题之前,您可以通过将 spring.main.register-shutdown-hook
设置为 false
来解决它。
与 described in the documentation 一样,SpringApplication.exit
旨在用于您的应用程序的 main
方法。您对它的使用很不寻常,您可能想探索采用不同的方法。
您似乎希望您的应用程序在迁移数据库然后关闭时处于 运行 模式。您当前的方法似乎不会限制在迁移数据库之前可能发生的事情。在启动应用程序时,您可以通过使用不同的 main @Configuration
class 来更好地控制它。像这样:
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
if (args.length > 0 && args[0].equals("migrate")) {
new SpringApplicationBuilder(DatabaseMigration.class).web(WebApplicationType.NONE).run(args);
}
else {
SpringApplication.run(ExampleApplication.class, args);
}
}
@ImportAutoConfiguration(classes = { FlywayAutoConfiguration.class, DataSourceAutoConfiguration.class })
static class DatabaseMigration {
}
}
以上,当以 --migrate
命令行参数启动时,将足以创建 DataSource
、运行 Flyway,然后退出。
我们有一个 spring-boot 服务,它需要在使用 flyway 进行一些数据库迁移后停止。我们通过回调和行
解决了这个问题int exitCode = SpringApplication.exit(applicationContext, () -> 0);
System.exit(exitCode);
到目前为止它运行良好 (spring-boot 2.4.x),但最近,我们升级到 2.5.1,现在它不再停止了。这是错误还是功能?现在还有其他方法可以关闭服务吗?
编辑
我做了一个线程转储,但我不太确定如何解释它
"main" #1 prio=5 os_prio=0 tid=0x00000000027eb000 nid=0x1280 in Object.wait() [0x000000000276d000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000006c29756c8> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1252)
- locked <0x00000006c29756c8> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1326)
at java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:107)
at java.lang.ApplicationShutdownHooks.run(ApplicationShutdownHooks.java:46)
at java.lang.Shutdown.runHooks(Shutdown.java:123)
at java.lang.Shutdown.sequence(Shutdown.java:170)
at java.lang.Shutdown.exit(Shutdown.java:216)
- locked <0x00000006c28bc680> (a java.lang.Class for java.lang.Shutdown)
at java.lang.Runtime.exit(Runtime.java:109)
at java.lang.System.exit(System.java:973)
at com.whatsoever.glp.moneypenny.config.PostMigrationCallback.supports(PostMigrationCallback.java:44)
at org.flywaydb.core.internal.callback.DefaultCallbackExecutor.execute(DefaultCallbackExecutor.java:115)
at org.flywaydb.core.internal.callback.DefaultCallbackExecutor.onMigrateOrUndoEvent(DefaultCallbackExecutor.java:73)
at org.flywaydb.core.internal.command.DbMigrate.migrate(DbMigrate.java:165)
at org.flywaydb.core.Flyway.execute(Flyway.java:216)
at org.flywaydb.core.Flyway.execute(Flyway.java:165)
at org.flywaydb.core.Flyway.execute(Flyway.java:572)
at org.flywaydb.core.Flyway.migrate(Flyway.java:165)
at org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:66)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=12=](AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda7/370475881.getObject(Unknown Source)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
- locked <0x00000006c295db30> (a java.util.concurrent.ConcurrentHashMap)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1154)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:908)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
- locked <0x00000006c2636ae8> (a java.lang.Object)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:338)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1343)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1332)
at com.whatsoever.glp.moneypenny.Application.main(Application.java:28)
"RMI TCP Accept-0" #13 daemon prio=5 os_prio=0 tid=0x000000001f040800 nid=0x1554 runnable [0x000000001fcff000]
java.lang.Thread.State: RUNNABLE
at java.net.DualStackPlainSocketImpl.accept0(Native Method)
at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
- locked <0x00000006c261d120> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:560)
at java.net.ServerSocket.accept(ServerSocket.java:528)
at sun.management.jmxremote.LocalRMIServerSocketFactory.accept(LocalRMIServerSocketFactory.java:52)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377)
at java.lang.Thread.run(Thread.java:748)
"SpringApplicationShutdownHook" #14 prio=5 os_prio=0 tid=0x0000000021126000 nid=0x630 waiting for monitor entry [0x0000000022e3f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:1021)
- waiting to lock <0x00000006c2636ae8> (a java.lang.Object)
at org.springframework.boot.SpringApplicationShutdownHook.closeAndWait(SpringApplicationShutdownHook.java:128)
at org.springframework.boot.SpringApplicationShutdownHook$$Lambda1/1430717967.accept(Unknown Source)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.boot.SpringApplicationShutdownHook.run(SpringApplicationShutdownHook.java:101)
at java.lang.Thread.run(Thread.java:748)
"RMI TCP Connection(1)-172.19.5.119" #15 daemon prio=5 os_prio=0 tid=0x00000000202bf000 nid=0x594 runnable [0x000000002163e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000006c26360a8> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run[=12=](TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda8/349364571.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001ec8f800 nid=0x1164 runnable [0x000000001f3be000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x00000006c2641f80> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x00000006c2641f80> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2.run(AppMainV2.java:48)
"RMI Scheduler(0)" #16 daemon prio=5 os_prio=0 tid=0x00000000202e0000 nid=0x1ac4 waiting on condition [0x000000002173e000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006c261cc28> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001d255000 nid=0x2370 in Object.wait() [0x000000001e78f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000006c2643898> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000006c2643898> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001d254000 nid=0x2ae8 in Object.wait() [0x000000001e68f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000006c261d9d8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000006c261d9d8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001ecf3000 nid=0x28a4 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001ece4800 nid=0x24e4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001ed29000 nid=0x27dc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001ed28000 nid=0x3170 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001ed1f800 nid=0x4108 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001d263000 nid=0x2980 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001d262000 nid=0x2144 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"VM Thread" os_prio=2 tid=0x000000001d250800 nid=0x4568 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000000000283e000 nid=0x1254 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000283f800 nid=0x4490 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002841000 nid=0x28b0 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002842800 nid=0x325c runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002846000 nid=0x1a18 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002847000 nid=0x35c0 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x000000000284a800 nid=0xdfc runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x000000000284b800 nid=0x2fc0 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001f04c000 nid=0x492c waiting on condition
JNI global references: 1665
我猜是关于这个 等待锁定 <0x00000006c28ae9a0>,但我不确定
编辑 2
这里是相关的类涉及
迁移完成后用于关闭服务的回调
@Component
public class PostMigrationCallback implements Callback {
private final FlywayCustomProperties flywayCustomProperties;
private final ApplicationContext applicationContext;
public PostMigrationCallback(FlywayCustomProperties flywayCustomProperties, ApplicationContext applicationContext) {
this.flywayCustomProperties = flywayCustomProperties;
this.applicationContext = applicationContext;
}
@Override
public boolean supports(Event event, Context context) {
if (event.getId().equalsIgnoreCase("afterMigrate") && flywayCustomProperties.isMigrationOnly()) {
log.info("Service is going to shutdown as configuration was set to spring.flyway.migrate-only=true and schema was migrated successfully");
int exitCode = SpringApplication.exit(applicationContext, () -> 0);
System.exit(exitCode);
}
return false;
}
@Override
public boolean canHandleInTransaction(Event event, Context context) {
return false;
}
@Override
public void handle(Event event, Context context) {
}
@Override
public String getCallbackName() {
return "MigrationOnlyCallback";
}
}
工厂配置回调
@Configuration
@ConditionalOnProperty(
value = "spring.flyway.enabled",
havingValue = "true"
)
public class FlywayFactory {
private PostMigrationCallback postMigrationCallback;
public FlywayFactory(PostMigrationCallback postMigrationCallback) {
this.postMigrationCallback = postMigrationCallback;
}
@Bean
public FlywayMigrationInitializer flywayInitializer(Flyway flyway) {
Flyway flywayWithCallback = Flyway.configure().configuration(flyway.getConfiguration()).callbacks(flywayCallback()).load();
return new FlywayMigrationInitializer(flywayWithCallback);
}
@Bean
public Callback flywayCallback() {
return postMigrationCallback;
}
}
感谢线程转储。问题是由 Spring 中的 a possible bug 引起的 Spring Boot 在刷新应用程序上下文时调用 System.exit(int)
时死锁。感谢您提请我们注意。
在 Spring 引导中解决问题之前,您可以通过将 spring.main.register-shutdown-hook
设置为 false
来解决它。
与 described in the documentation 一样,SpringApplication.exit
旨在用于您的应用程序的 main
方法。您对它的使用很不寻常,您可能想探索采用不同的方法。
您似乎希望您的应用程序在迁移数据库然后关闭时处于 运行 模式。您当前的方法似乎不会限制在迁移数据库之前可能发生的事情。在启动应用程序时,您可以通过使用不同的 main @Configuration
class 来更好地控制它。像这样:
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
if (args.length > 0 && args[0].equals("migrate")) {
new SpringApplicationBuilder(DatabaseMigration.class).web(WebApplicationType.NONE).run(args);
}
else {
SpringApplication.run(ExampleApplication.class, args);
}
}
@ImportAutoConfiguration(classes = { FlywayAutoConfiguration.class, DataSourceAutoConfiguration.class })
static class DatabaseMigration {
}
}
以上,当以 --migrate
命令行参数启动时,将足以创建 DataSource
、运行 Flyway,然后退出。