【问题标题】:Spring Boot app cannot connect to Redis Replica in DockerSpring Boot 应用程序无法连接到 Docker 中的 Redis 副本
【发布时间】: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);
    }
}

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

【问题讨论】:

    标签: spring-boot docker redis lettuce


    【解决方案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 端口。
    • 你说得对,有趣的是我知道但对这个问题完全视而不见,非常感谢,只需要一双单独的眼睛
    • 能否更新您的答案以便我接受?
    猜你喜欢
    • 1970-01-01
    • 2020-04-18
    • 2020-12-23
    • 2021-05-03
    • 2019-08-28
    • 2020-11-21
    • 2021-12-23
    • 2020-07-29
    • 1970-01-01
    相关资源
    最近更新 更多