RedisCommandExecutionException: MOVED **** {IP}:{PORT} 에러

두별·2023년 12월 10일
2

TIL

목록 보기
43/46

상황

  • 개발 서버에서는 정상적이였던 Redis Config가 운영서버에서는 오류를 발생시켰다.
  • 해당 오류는 애플리케이션 내에서 Redis에 get 접근을 할 때 발생한다.
023-11-20 17:37:51.859 ERROR 20595 --- [reactor-http-nio-4] c.p.g.exception.GlobalExceptionHandler : Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: MOVED **** {IP}:{PORT}

org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: MOVED **** {IP}:{PORT}
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54) ~[spring-data-redis-2.7.6.jar!/:2.7.6]

원인

  • 개발 서버와 운영 서버 Redis 환경이 다르다. 개발 서버는 Docker로 Redis에 별다른 세팅 없이 Stand Alone 모드로 사용하고 있었고, 운영 서버는 Amazon ElastiCache로 Cluster 모드를 사용하고 있었다. 개발과 운영이 서로 다른 Redis 모드로 세팅이 되어 있는데, Redis Config는 Stand Alone 모드에 대한 설정만 되어있어 발생한 오류였다.

  • Redis 에는 모드가 3가지로 분류 된다.

    • Stand Alone (현재 개발 서버 세팅)

    • Sentinel
      감시자는 Master Node가 죽으면 감시자들끼리 복제 Node를 Master Node로 승격시킬지 찬반 투표를 하여 다수결로 Master Node를 뽑아낸다. 때문에 투표를 위해 Sentinel는 홀수를 권장하고, 최소 3개 이상이여야 한다.

    • Cluster (운영 서버 세팅)
      데이터 양이 많을 수록 성능이 떨어질 수 밖에 없으므로 데이터를 분리 저장하는 것이 샤딩이다.
      Cluster는 16384개의 Hash Slot에 데이터를 저장할 수 있고, 각 Shard가 N/1만큼 분할 저장한다.
      데이터를 저장할 때, Key를 Redis 내부 해쉬 함수가 해쉬값으로 뽑아내고 해당하는 해쉬슬롯이 있는 Shard에 데이터를 저장한다.
      데이터를 읽어올 때, 마찬가지로 Key를 해쉬값으로 뽑아낸 후 접근한 Shard에 없으면, Client한테 몇번 해쉬슬롯에 있는지 알려준다. Client는 Redirection 받은 위치로 다시 Redis에 요청하여 해당 데이터를 읽어온다.

해결

에러문구에 MOVED는, Cluster 에서 "해당 노드에 니가 찾는 데이터가 없다. 니가 찾는 데이터는 이 노드에 있으니 여기로 다시 요청해봐" 라고 알려주는 Redirection 응답이다.
그래서 해당 해쉬슬롯 번호와 해쉬슬롯이 존재하는 노드의 IP, PORT 정보가 같이 응답으로 오는 것이다.
개발 서버 세팅은 Stand Alone 모드로 세팅이 되어 있으니, "MOVED? 그게 뭔데?" 하며 이 응답을 처리하지 못한다. 때문에 운영 환경일 경우에는 MOVED Redirection을 처리하기 위해 Cluster 설정으로 바꿔주어 해결하였다.

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        if (profileUtil.isProd()) {
            RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
            clusterConfiguration.clusterNode(host, port);
            return new LettuceConnectionFactory(clusterConfiguration);
        }
        return new LettuceConnectionFactory(host, port);
    }

1개의 댓글

comment-user-thumbnail
2024년 2월 7일

감사합니다.

답글 달기