简体   繁体   English

Spring-Boot 2.7 Redis PUB/SUB 在缺少 Redis 连接时启动失败

[英]Spring-Boot 2.7 Redis PUB/SUB fails startup on missing Redis connection

I have this configuration for my pub/sub implementation:我的 pub/sub 实现有这个配置:

@Bean
public RedisMessageListenerContainer container(LettuceConnectionFactory connectionFactory,
                                               MessageListenerAdapter listenerAdapter) {

    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.addMessageListener(listenerAdapter, new ChannelTopic(publishChannel));
    return container;
}

@Bean
public MessageListenerAdapter listenerAdapter(RedisReceiver receiver) {
    return new MessageListenerAdapter(receiver, "receiveMessage");
}

@Bean
public StringRedisTemplate template(LettuceConnectionFactory connectionFactory) {
    return new StringRedisTemplate(connectionFactory);
}

This code worked fine until I updated to Spring-Boot 2.7 (previously 2.6.7).在我更新到 Spring-Boot 2.7(以前是 2.6.7)之前,这段代码运行良好。 Now this code throws the following error on startup, when my Redis is not running:现在,当我的 Redis 未运行时,此代码在启动时会引发以下错误:

Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean ' container ';上下文初始化期间遇到异常 - 取消刷新尝试:org.springframework.context.ApplicationContextException:无法启动bean'容器'; nested exception is org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis;嵌套异常是 org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to localhost/:6379嵌套异常是 io.lettuce.core.RedisConnectionException: Unable to connect to localhost/:6379

(" container " is the Bean at the top in my code snippet) (“容器”是我的代码片段顶部的 Bean)

Where or how can I configure that it catches the thrown exception on startup and just retries it again until the connection to Redis is available?我可以在哪里或如何配置它在启动时捕获抛出的异常并再次重试,直到与 Redis 的连接可用?

UPDATE:更新:

Did some digging and this part of the code is the culprit:做了一些挖掘,这部分代码是罪魁祸首:

https://github.com/spring-projects/spring-data-redis/blob/main/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java#L1169 https://github.com/spring-projects/spring-data-redis/blob/main/src/main/java/org/springframework/data/redis/listener/RedisMessageListenerContainer.java#L1169

In comparison to the 2.6.x branch, where this was inside a try/catch (not sure though as it seems to be a completely different implementation), it is missing in version 2.7.与 2.6.x 分支相比,它位于 try/catch 中(不确定,因为它似乎是一个完全不同的实现),它在 2.7 版中缺失。

我认为你必须等待下一个版本,有人已经修复它并刚刚合并到 maim

It looks like the PR didn't fix the issue in the latest release (2.7.1), it really "works as intended" to Spring team :\看起来PR没有在最新版本 (2.7.1) 中解决这个问题,它对 Spring 团队来说确实“按预期工作”:\

Here's my workaround:这是我的解决方法:

RedisMessageListenerContainer.Subscriber#initialize(...) method is called when adding listener to the container, so what you need is to make sure the redis connection is available first, and then add the listener to container. RedisMessageListenerContainer.Subscriber#initialize(...)方法是在容器添加监听器时调用的,所以需要先确保redis连接可用,然后再将监听器添加到容器中。

@AllArgsConstructor
public class MessageListenerSubscriber implements ApplicationListener<ApplicationReadyEvent> {

  private RedisConnectionFactory connectionFactory;
  private RedisMessageListenerContainer container;
  private MessageListener listener;
  private Collection<? extends Topic> topics;

  @Async
  @Override
  public void onApplicationEvent(ApplicationReadyEvent event) {
    var template = new RetryTemplateBuilder()
      .maxAttempts(Integer.MAX_VALUE)
      .fixedBackoff(5000)
      .build();
    template.execute(context -> {
      try {
        var connection = connectionFactory.getConnection();
        if (connection.isSubscribed()) {
          log.debug("Retrieved connection is already subscribed; aborting listening");
          return null;
        }
      } catch (Exception e) {
        log.error("Connection failure occurred. Restarting subscription task after 5000 ms");
        throw e;
      }
      this.container.addMessageListener(listener, topics);
      log.debug("Listeners registered successfully after {} retries.", context.getRetryCount());
      return null;
    });
  }
}

and the configuration looks like:配置如下:

@Bean
public RedisMessageListenerContainer container(LettuceConnectionFactory connectionFactory) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    return container;
}

@Bean
public MessageListenerAdapter listenerAdapter(RedisReceiver receiver) {
    return new MessageListenerAdapter(receiver, "receiveMessage");
}

@Bean
public MessageListenerSubscriber messageListenerSubscriber(
    RedisConnectionFactory connectionFactory,
    RedisMessageListenerContainer container,
    MessageListenerAdapter listener){
  return new MessageListenerSubscriber(
    connectionFactory,
    container,
    listener,
    List.of(new ChannelTopic(publishChannel))
  );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM