During migration Spring Boot from version 2.6.2 to 3.1, I was switching also spring-data-redis from 2.6.2 to 3.1 and have encountered problem that Message Listener bean can not be started anymore. Exception itself does not describe too much as it only says that:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'jedisMessageListenerContainer'","logger_name":"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"}
Where jedisMessageListenerContainer
looks as following:
@Bean
RedisMessageListenerContainer jedisMessageListenerContainer(JedisConnectionFactory jedisConnectionFactory,
RedisKeyExpirationListener redisListener) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(jedisConnectionFactory);
container.addMessageListener(redisListener, new PatternTopic(
"__keyevent@0__:expired"));
container.setErrorHandler(e -> log.error("There was an error in redis key expiration listener container", e));
return container;
}
RedisKeyExpirationListener
is a class that implements MessageListener
interface from the spring-data-redis
After some debugging I have find out only that RedisMessageListenerContainer
is catching TimeoutException
and then throwing
IllegalStateException("Subscription registration timeout exceeded", e);
Which is later being caught by Bean Processor and parsed into Failed to start bean
exception message.
I have tried to increase the MaxSubscriptionRegistrationWaitingTime
for the container (up to 60 seconds) but it just resulted in longer waiting time for same exception.
I am using the Message Listener together with Jedis (version 4.4.2 but also checked with 4.3.2 which should be compatible) as the connection factory and spring-boot-starter-data-redis
with version 3.1.0.
Here is implementation of the connection factory:
@Bean
public JedisPoolConfig jedisPoolConfig() {
var jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(10);
jedisPoolConfig.setMinIdle(0);
jedisPoolConfig.setMaxIdle(8);
return jedisPoolConfig;
}
@Bean
public JedisConnectionFactory jedisConnectionFactory(JedisPoolConfig jedisPoolConfig) {
var jedisClientConfiguration = JedisClientConfiguration.builder()
.usePooling()
.poolConfig(jedisPoolConfig)
.and()
.readTimeout(Duration.ofMillis(1000L))
.useSsl()
.build();
As additional test I have just removed the addMessageListener
invocation on the Redis container and application was able to work with the Redis itself. The problem only starts to appear when I add the Message Listener
to the RedisMessageListenerContainer
.
Also when bringing back spring to version 2.6.2 together with spring-data-redis Message Listener
works and I can see that expired events are being handled.
Can someone help why this happening and why I cannot initialize the beans once want to add this listener?
I tried to:
@Bean
RedisKeyExpirationListener redisListener() {
return new RedisKeyExpirationListener();
}
@Bean
MessageListenerAdapter redisListenerAdapter(RedisKeyExpirationListener redisListener) {
return new MessageListenerAdapter(redisListener, "handleMessage");
}
@Bean
RedisMessageListenerContainer jedisMessageListenerContainer(JedisConnectionFactory jedisConnectionFactory,
MessageListenerAdapter redisListenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(jedisConnectionFactory);
container.addMessageListener(redisListenerAdapter, new PatternTopic(
"__keyevent@0__:expired"));
container.setErrorHandler(e -> log.error("There was an error in redis key expiration listener container", e));
return container;
}
where now RedisKeyExpirationListener
does implement own interface:
public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Message message, byte[] pattern);
}
instead implementing MessageListener
interface from the spring-data-redis
If you are expierencing Failed to start bean exception message
during local testing of Redis functionalities, try to comment .useSSl() in jedisConnectionFactory class.