Spring Boot 应用程序无法连接到 Docker 中的 Redis 副本

Posted

技术标签:

【中文标题】Spring Boot 应用程序无法连接到 Docker 中的 Redis 副本【英文标题】:Spring Boot app cannot connect to Redis Replica in Docker 【发布时间】:2021-10-23 20:51:08 【问题描述】:

我在 Docker 中遇到了一个奇怪的 Redis 连接问题。 我有一个带有主副本配置的简单 Spring Boot 应用程序。 以及我用来启动 Redis Master 和 Redis Replica 的 docker-compose 配置。

如果我通过 docker-compose 和 Spring Boot 应用程序将 Redis 作为 Docker 之外的简单 Java 进程启动,那么一切正常。它可以通过localhost成功连接到Master和Replica。

如果我将 Spring Boot 应用程序作为 Docker 容器与 Redis 容器一起启动,它可以成功连接到 Master,但不能连接到 Replica。这意味着我可以写入和读取主节点,但是当我尝试从副本读取时,我收到以下错误:

redis-sample-app_1  | Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: redis-replica-a/172.31.0.2:7001
redis-sample-app_1  | Caused by: java.net.ConnectException: Connection refused
redis-sample-app_1  |   at sun.nio.ch.SocketChannelImpl.checkConnect(Native Method) ~[na:1.8.0_302]

在 redis.conf 中,我更改了以下内容以将其绑定到所有网络接口:

bind * -::*
protected-mode no

docker-compose.yml

version: "3"

services:
  redis-master:
    image: redis:alpine
    command: redis-server --include /usr/local/etc/redis/redis.conf
    volumes:
      - ./conf/redis-master.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "6379:6379"

  redis-replica-a:
    image: redis:alpine
    command: redis-server --include /usr/local/etc/redis/redis.conf
    volumes:
      - ./conf/redis-replica.conf:/usr/local/etc/redis/redis.conf
    ports:
      - "7001:6379"

  redis-sample-app:
    image: docker.io/library/redis-sample:0.0.1-SNAPSHOT
    environment:
      - SPRING_REDIS_HOST=redis-master
      - SPRING_REDIS_PORT=6379
      - SPRING_REDIS_REPLICAS=redis-replica-a:7001
    ports:
      - "9080:8080"
    depends_on:
      - redis-master
      - redis-replica-a

application.yml

spring:
  redis:
    port: 6379
    host: localhost
    replicas: localhost:7001

RedisConfig.java

@Configuration
class RedisConfig 

    private static final Logger LOG = LoggerFactory.getLogger(RedisConfig.class);

    @Value("$spring.redis.replicas:")
    private String replicasProperty;

    private final RedisProperties redisProperties;

    public RedisConfig(RedisProperties redisProperties) 
        this.redisProperties = redisProperties;
    

    @Bean
    public StringRedisTemplate masterReplicaRedisTemplate(LettuceConnectionFactory connectionFactory) 
        return new StringRedisTemplate(connectionFactory);
    

    @Bean
    public LettuceConnectionFactory masterReplicaLettuceConnectionFactory(LettuceClientConfiguration lettuceConfig) 
        LOG.info("Master: :", redisProperties.getHost(), redisProperties.getPort());
        LOG.info("Replica property: ", replicasProperty);

        RedisStaticMasterReplicaConfiguration configuration = new RedisStaticMasterReplicaConfiguration(redisProperties.getHost(), redisProperties.getPort());

        if (StringUtils.hasText(replicasProperty)) 
            List<RedisURI> replicas = Arrays.stream(this.replicasProperty.split(",")).map(this::toRedisURI).collect(Collectors.toList());
            LOG.info("Replica nodes: ", replicas);
            replicas.forEach(replica -> configuration.addNode(replica.getHost(), replica.getPort()));
        

        return new LettuceConnectionFactory(configuration, lettuceConfig);
    

    @Scope("prototype")
    @Bean(destroyMethod = "shutdown")
    ClientResources clientResources() 
        return DefaultClientResources.create();
    

    @Scope("prototype")
    @Bean
    LettuceClientConfiguration lettuceConfig(ClientResources dcr) 
        ClientOptions options = ClientOptions.builder()
                .timeoutOptions(TimeoutOptions.builder().fixedTimeout(Duration.of(5, ChronoUnit.SECONDS)).build())
                .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .autoReconnect(true)
                .build();

        return LettuceClientConfiguration.builder()
                .readFrom(ReadFrom.REPLICA_PREFERRED)
                .clientOptions(options)
                .clientResources(dcr)
                .build();
    

    @Bean
    StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) 
        return new StringRedisTemplate(redisConnectionFactory);
    

    private RedisURI toRedisURI(String url) 
        String[] split = url.split(":");
        String host = split[0];
        int port;
        if (split.length > 1) 
            port = Integer.parseInt(split[1]);
         else 
            port = 6379;
        
        return RedisURI.create(host, port);
    

请告知如何继续进行故障排除。

【问题讨论】:

【参考方案1】:

在 docker 网络中运行所有内容(redis、replica 和 spring)时,您应该使用端口 6379 而不是 7001

7001 端口可用于从容器外部连接到它。但是现在您正在尝试从容器连接到容器。

所以把你的环境变量改成

SPRING_REDIS_REPLICAS=redis-replica-a:6379

【讨论】:

我在 docker-compose 文件中传递这些参数,否则,它将连接到 localhost,但是,从错误中可以看出,它正在尝试连接到 redis-replica-a/172.31 .0.2:7001 环境:- SPRING_REDIS_HOST=redis-master - SPRING_REDIS_PORT=6379 - SPRING_REDIS_REPLICAS=redis-replica-a:7001 @endryha 你是对的,我没有正确读取环境变量,我的错。尝试使用 SPRING_REDIS_REPLICAS=redis-replica-a:6379 。 7001 是从外部连接到容器的端口。在网络内部,使用了 6379 端口。 你说得对,有趣的是我知道但对这个问题完全视而不见,非常感谢,只需要一双单独的眼睛 能否更新您的答案以便我接受?

以上是关于Spring Boot 应用程序无法连接到 Docker 中的 Redis 副本的主要内容,如果未能解决你的问题,请参考以下文章

无法将 Spring Boot 应用程序连接到 IBM Informix 数据库

无法从 Spring Boot 应用程序连接到 Bigtable

Docker 上的 Spring Boot 无法连接到 MySQL

对无法连接到 MySQL 的 Spring Boot 应用程序进行故障排除

Angular 无法连接到 docker compose 中的 Spring Boot 端点

docker-compose spring boot无法连接到Postgres [重复]