spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.46k stars 40.76k forks source link

Failed to start bean 'springSessionRedisMessageListenerContainer'; nested exception is java.lang.IllegalStateException: Subscription registration timeout exceeded. #31138

Closed leshalv closed 2 years ago

leshalv commented 2 years ago

springboot 2.7.0

upgrade to springboot 2.7.0 Project could not be started

Failed to start bean 'springSessionRedisMessageListenerContainer'; nested exception is java.lang.IllegalStateException: Subscription registration timeout exceeded.
pruidong commented 2 years ago

Registration timeouts are generally misconfigurations. You can check your config file. Or provide a minimal example.

leshalv commented 2 years ago

Looking at the update log, it should be caused by refactoring RedisMessageListenerContainer

wilkinsona commented 2 years ago

@leshalv RedisMessageListenerContainer is part of Spring Data Redis which is managed as a separate project. Please open an issue with them. We can re-open this issue if it turns out that a change in Spring Boot is necessary to accommodate some changes in Spring Data Redis. If you open a Spring Data Redis issue, please take the time to provide a complete yet minimal sample that reproduces the problem.

sylvermeister commented 2 years ago

Hello! I also experienced this by just upgrading the spring-boot-starter-parent to 2.7.0

org.springframework.context.ApplicationContextException: Failed to start bean 'springSessionRedisMessageListenerContainer'; nested exception is java.lang.IllegalStateException: Subscription registration timeout exceeded.
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
    at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
    at java.lang.Iterable.forEach(Iterable.java:75)
    at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
    at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295)
        at (omitted project)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
    at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
    at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:65)
Caused by: java.lang.IllegalStateException: Subscription registration timeout exceeded.
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.lazyListen(RedisMessageListenerContainer.java:274)
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.start(RedisMessageListenerContainer.java:248)
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
    ... 22 common frames omitted
Caused by: java.util.concurrent.TimeoutException: null
    at java.util.concurrent.CompletableFuture.timedGet(CompletableFuture.java:1784)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1928)
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.lazyListen(RedisMessageListenerContainer.java:268)
    ... 24 common frames omitted
wilkinsona commented 2 years ago

@sylvermeister Perhaps you can provide a minimal sample that both @mp911de and I have asked for. If you can, please comment on https://github.com/spring-projects/spring-session/issues/2098 attaching it or linking to it.

mp911de commented 2 years ago

FWIW, when using the Lettuce driver, you can enable debug logging for the io.lettuce category to capture what Redis commands are issued and what responses you receive. With Spring Data Redis 2.7, we rewrote RedisMessageListenerContainer to await subscription confirmation from Redis to avoid race conditions.

leshalv commented 2 years ago

Subscription registration timeout exceeded.

rowi1de commented 2 years ago

FWIW, when using the Lettuce driver, you can enable debug logging for the io.lettuce category to capture what Redis commands are issued and what responses you receive. With Spring Data Redis 2.7, we rewrote RedisMessageListenerContainer to await subscription confirmation from Redis to avoid race conditions.

@mp911de could you give some more details about the rewrite? What does "await subscription confirmation" mean? I even increased to subscription timeout to crazy levels and it will always time out. Would be good to know what changes are required. I'm getting failures by just upgrading from 2.6.x to 2.7. EDIT: Found https://github.com/spring-projects/spring-data-commons/wiki/Spring-Data-2021.2-(Raj)-Release-Notes#revised-redismessagelistenercontainer

AmanMoglix commented 4 months ago

Need to update things

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.17.3</version>
        </dependency>
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.Jedis;

@Configuration
public class RedisConfig {

    @Autowired
    private ApplicationConfig applicationConfig;
    @Autowired
    @Lazy
    private UserCoinHistoryRepository userCoinHistoryRepository;
    @Autowired
    @Lazy
    private CoinTransactionFactory coinTransactionFactory;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    @Lazy
    private RedisTemplate < String, Object > redisTemplate;

    @Bean
    public RedisTemplate<String, Object> redisTemplateJedis(RedisConnectionFactory cf) {
        RedisTemplate<String, Object> redisTemplateJedis = new RedisTemplate<String, Object>();
        redisTemplateJedis.setConnectionFactory(cf);
        redisTemplateJedis.setKeySerializer(new StringRedisSerializer());
        redisTemplateJedis.setValueSerializer(new StringRedisSerializer());
        redisTemplateJedis.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(Object.class));
        return redisTemplateJedis;
    }

    @Bean
    public MessageListenerAdapter messageListenerAdapter() {
        return new MessageListenerAdapter(new RedisKeyExpirationListener(userCoinHistoryRepository,coinTransactionFactory,redissonClient,redisTemplate));
    }

    /**
     * Configures a RedisMessageListenerContainer bean that sets up a Redis message listener.
     * This container listens for keyspace notifications on all keys that match the pattern "__key*__:*".
     *
     * @param connectionFactory the factory to establish Redis connections
     * @param messageListenerAdapter the adapter that handles the messages received
     * @return the configured RedisMessageListenerContainer
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
                                                                       MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(messageListenerAdapter, new PatternTopic("__key*__:*"));
        return container;
    }
    @Bean
    public Jedis jedisClient() {
        return new Jedis(applicationConfig.getRedisServer(),applicationConfig.getRedisPort());
    }
}