Problems encountered in upgrading springboot from 1.4.7 to 2.2.7:
I have some non-web projects, and When I use spring boot 1.4.7, they can run normally, but when I upgrade the spring boot version to 2.2.7, they can start but will exit immediately. I read the wiki page [https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.0-Migration-Guide], and use spring.main.web-application-type=none to instead of spring.main.web-environment=false , but it does't resolve this problem.
My pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<flowgate.version>1.1.0</flowgate.version>
<jackson.version>2.10.0</jackson.version>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
I created a new non-web project through the website start.spring.io, when I run the project ,it has the same problem
Comment From: bclozel
What do you mean by "it ran normally"? The behavior you're describing is expected: if no non-daemon thread is present (typically a running server), then the application exits as soon as it's done. What is your application doing? Does it have CommandLineRunner
or ApplicationRunner
instances?
Could you provide a sample application as a repository or a zip file showing how work is performed in your application? Other than that, the behavior you're describing seems fine.
Comment From: Pengpengwanga
Thanks for your replay. These projects integrate Redis, and use Redis's pub/sub mode as MQ, they receive messages from Redis, and then process the corresponding queue. I think there should be one non-daemon thread at least to used listen the Redis topic. the application shouldn't exit. It run normally is that when I use spring boot 1.4.7 it will not auto exit, but when I change the spring boot version to 2.2.7 it will auto exit. BTW, I don't use the ApplicationRunner or CommandLineRunner in my project. My confusion is that I do not change any code except upgrade the version of spring boot.
My redis config class.
@Configuration
public class RedisConfig {
@Value("${redis.topic:infoblox}")
private String commandTopic;
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new ChannelTopic(commandTopic));
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(MessageReceiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
@Bean
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
}
Comment From: bclozel
I guess this is a consequence of Spring Data Redis moving from Jedis to Lettuce as its main driver, see #10480. Jedis is using commons-pool2 and a blocking arrangement when listening for messages. In this case, something is keeping the application alive but it's mostly incidental complexity.
With Spring Boot 1.5.0:
"Common-Cleaner" #10 daemon prio=8 os_prio=31 cpu=0.54ms elapsed=5.33s tid=0x00007fd635812800 nid=0xa603 in Object.wait() [0x000070000c725000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(java.base@12.0.2/Native Method)
- waiting on <0x000000070e704460> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(java.base@12.0.2/ReferenceQueue.java:155)
- locked <0x000000070e704460> (a java.lang.ref.ReferenceQueue$Lock)
at jdk.internal.ref.CleanerImpl.run(java.base@12.0.2/CleanerImpl.java:148)
at java.lang.Thread.run(java.base@12.0.2/Thread.java:835)
at jdk.internal.misc.InnocuousThread.run(java.base@12.0.2/InnocuousThread.java:134)
"commons-pool-EvictionTimer" #18 daemon prio=5 os_prio=31 cpu=0.13ms elapsed=4.42s tid=0x00007fd634898000 nid=0x6403 in Object.wait() [0x000070000d246000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(java.base@12.0.2/Native Method)
- waiting on <0x000000070d334720> (a java.util.TaskQueue)
at java.util.TimerThread.mainLoop(java.base@12.0.2/Timer.java:553)
- locked <0x000000070d334720> (a java.util.TaskQueue)
at java.util.TimerThread.run(java.base@12.0.2/Timer.java:506)
"container-1" #21 prio=5 os_prio=31 cpu=15.55ms elapsed=4.21s tid=0x00007fd63784a000 nid=0x9303 runnable [0x000070000d44c000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(java.base@12.0.2/Native Method)
at java.net.SocketInputStream.socketRead(java.base@12.0.2/SocketInputStream.java:115)
at java.net.SocketInputStream.read(java.base@12.0.2/SocketInputStream.java:168)
at java.net.SocketInputStream.read(java.base@12.0.2/SocketInputStream.java:140)
at java.net.SocketInputStream.read(java.base@12.0.2/SocketInputStream.java:126)
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:196)
at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)
at redis.clients.jedis.Protocol.process(Protocol.java:151)
at redis.clients.jedis.Protocol.read(Protocol.java:215)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
at redis.clients.jedis.Connection.getRawObjectMultiBulkReply(Connection.java:285)
at redis.clients.jedis.BinaryJedisPubSub.process(BinaryJedisPubSub.java:87)
at redis.clients.jedis.BinaryJedisPubSub.proceed(BinaryJedisPubSub.java:82)
at redis.clients.jedis.BinaryJedis.subscribe(BinaryJedis.java:3070)
at org.springframework.data.redis.connection.jedis.JedisConnection.subscribe(JedisConnection.java:3182)
at org.springframework.data.redis.listener.RedisMessageListenerContainer$SubscriptionTask.eventuallyPerformSubscription(RedisMessageListenerContainer.java:790)
at org.springframework.data.redis.listener.RedisMessageListenerContainer$SubscriptionTask.run(RedisMessageListenerContainer.java:746)
at java.lang.Thread.run(java.base@12.0.2/Thread.java:835)
With Spring Boot 2.0.0, we're using deamon threads and event loops to listen for messages, so not a blocking socket:
"lettuce-nioEventLoop-4-1" #25 daemon prio=5 os_prio=31 cpu=33.07ms elapsed=1.94s tid=0x00007f9de9a23800 nid=0x7203 runnable [0x000070000e1ba000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.KQueue.poll(java.base@12.0.2/Native Method)
at sun.nio.ch.KQueueSelectorImpl.doSelect(java.base@12.0.2/KQueueSelectorImpl.java:122)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@12.0.2/SelectorImpl.java:124)
- locked <0x000000070ff59d30> (a sun.nio.ch.Util$2)
- locked <0x000000070ff59b58> (a sun.nio.ch.KQueueSelectorImpl)
at sun.nio.ch.SelectorImpl.select(java.base@12.0.2/SelectorImpl.java:136)
at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:753)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:409)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(java.base@12.0.2/Thread.java:835)
"lettuce-nioEventLoop-4-2" #26 daemon prio=5 os_prio=31 cpu=16.54ms elapsed=1.87s tid=0x00007f9dec1cf800 nid=0x7403 runnable [0x000070000e2bd000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.KQueue.poll(java.base@12.0.2/Native Method)
at sun.nio.ch.KQueueSelectorImpl.doSelect(java.base@12.0.2/KQueueSelectorImpl.java:122)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@12.0.2/SelectorImpl.java:124)
- locked <0x000000070ff6e8f8> (a sun.nio.ch.Util$2)
- locked <0x000000070ff6e7a0> (a sun.nio.ch.KQueueSelectorImpl)
at sun.nio.ch.SelectorImpl.select(java.base@12.0.2/SelectorImpl.java:136)
at io.netty.channel.nio.NioEventLoop.select(NioEventLoop.java:753)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:409)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:886)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(java.base@12.0.2/Thread.java:835)
In this case, the best way to keep your application running is to either:
- Add
@EnableScheduling
and@Scheduled
tasks, if your application needs such tasks - Create your own
ThreadPoolTaskExecutor
bean, configure it to use non-daemon threads (ThreadPoolTaskExecutor#setDaemon
) and configure it on yourRedisMessageListenerContainer
In both cases, closing the application context is the correct way to shut down the application and its resources. If you'd like to upgrade your application, please upgrade from one minor version to the next and don't forget to check out the release notes in our wiki (even though in this case it wouldn't have helped).
Thanks!
Comment From: Pengpengwanga
Thank you very much.