[Spring Boot] Redis Multi Database 연결

Monday·2024년 1월 25일

Spring

목록 보기
3/6
post-thumbnail

1. 상황

Redis는 한 서버에 0-15번 까지 총 16개의 Database를 가질 수 있다.
(http://redisgate.kr/redis/configuration/param_databases.php)

현재 개발중인 프로젝트에서 0,1 번 2개의 Database를 사용하고자 했다.

2. 처음 코드

	@Value("${project.redis.host}")
    private String host;

    @Value("${project.redis.port}")
    private int port;

	@Bean
    @Primary
    LettuceConnectionFactory connectionFactory1() {
        return createConnectionFactoryWith(0);
    }
    
    @Bean
    StringRedisTemplate redisTemplate1(LettuceConnectionFactory connectionFactory1) {
        return new StringRedisTemplate(connectionFactory1);
    }
    
    @Bean
    LettuceConnectionFactory connectionFactory2() {
        return createConnectionFactoryWith(1);
    }
    
    @Bean
    StringRedisTemplate redisTemplate2(LettuceConnectionFactory connectionFactory2) {
        return new StringRedisTemplate(connectionFactory2);
    }
    
    public LettuceConnectionFactory createConnectionFactoryWith(int index) {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        redisStandaloneConfiguration.setDatabase(index);

        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }

3. 문제점

3.1 @Primary 부재 시 문제점


Primary 어노테이션 없이 코드를 실행시킬 경우 위의 에러를 볼 수 있다
설정과정에 내부적인 어딘가에서 redisTemplate 이라는 이름의 Bean을 찾으려는데
우리가 redisTemplate1,2 이런식으로 Bean을 등록해서 생기는 문제 같다

LettuceConnection Factory 조회

3.1.1 그렇다면 이름을 redisTemplate으로 변경하면?

그렇게 하면 위의 오류가 발생하지 않기는 한다.
하지만 현재 RedisTemplate 이 2개가 필요한 상황인데 Config 클래스에
같은 이름의 Method가 2개가 존재할 수 없다.
설령 다른 Class로 분리한다고 해도 어짜피 redisTemplate 이라는
이름의 Bean을 2개는 만들 수 없을것이다.
그렇기에 같은 Class 에서 다른 이름의 Method로 정의하였다.

3.1.2 이름을 redisTemplate으로 변경해도 발생하는 에러


위 처럼 Auto Configure를 할때 RedisConnectionFactory(Jedis, Lettuce)가 필요한데
우리는 2개의 Database에 연결하기 위해 ConnectionFactory가 2개라서
의존성 주입을 할려는데 둘 중 어느것을 주입할 것인지 선택을 못 한다.
이 경우 @Primary 로 우선순위(기본값)을 설정해주거나
@Qualifier로 같은 타입의 Bean 들을 구분해주면 되는데
Auto Configure 과정에 발생하는 에러여서 @Qualifier 를 적용시키기
어렵다고 판단이 들어 @Primary를 사용하였다.

3.2 RedisTemplate 생성시 ReidsConnectionFactory 주입 관련 문제

이제 더 이상 RunTime에 Error가 발생하지 않아서 괜찮겠지 했지만
또 문제가 발생했다

1번 Database에 아무 데이터도 들어가지 않는 것이다.

어디갔나 했더니 0번 데이터베이스에 사이 좋게 들어가 있었다...

3.2.1 @Primary 존재로 인한 문제

@Primary 어노테이션의 존재로 인하여 RedisConnectionFactory 타입의 Bean을 주입할 일이 생기면 이름과 관계 없이 0번 데이터베이스 설정에
해당되는 connectionFactory1를 기본으로 주입하게 되는것이다...
보다 명확하게 확인을 하기 위해 log를 찍어서 확인해 보았다.

    @Bean
    StringRedisTemplate redisTemplate1(LettuceConnectionFactory connectionFactory1) {
     	log.info("connectionFactory1 : {}", connectionFactory1);
        log.info("connectionFactory1 method call : {}", connectionFactory1());
        
        return new StringRedisTemplate(connectionFactory1);
    }
    
    @Bean
    StringRedisTemplate redisTemplate2(LettuceConnectionFactory connectionFactory2) {
     	log.info("connectionFactory2 : {}", connectionFactory2);
        log.info("connectionFactory2 method call : {}", connectionFactory2());
        
        return new StringRedisTemplate(connectionFactory2);
    }
   

위와 같이 매개변수로 주입되는 RedisConnectionFactory의 객체 정보와
직접 Method Call을 했을때 반환 받게 되는 객체의 정보를 출력해 보았다.

결과는 역시 다음과 같았다.
처음 2번은 connectionFactory1을 주입받고, connectionFactory1() 의
반환값이기에 같은게 맞다.
하지만 문제는 3번째 log이다. 3번째 log의 경우 우리가 기대하는 것은
connectionFactory2 를 주입 받는 것인데, 첫번째 두번째와 같은 해시코드를 가진다
즉 redisTemplate2가 생성될때 매개변수로 connectionFactory1이 주입되고
그 결과 redisTemplate2 또한 0번 Database와 연결되어 있는 것이였다.

3.2.2 해결방안

1) 메서드 직접 호출

4번째 log를 보면 직접 Method를 호출하는 경우에는 우리가 원하는 connectionFactory2의 객체를 받을 수 있다.
그래서 다음과 같이 수정하면 우리가 원하는 대로 redisTemplate2를 생성할때
connectionFactory2 객체를 매개변수로 전달할 수 있다.

    @Bean
    StringRedisTemplate redisTemplate1() { 
        return new StringRedisTemplate(connectionFactory1());
    }
    
    @Bean
    StringRedisTemplate redisTemplate2() { 
        return new StringRedisTemplate(connectionFactory2());
    }

외부에서 의존성을 주입 받기 보다는 원하는 객체를 사용하기 위해
우리가 직접 Method 를 Call 하는 것이다.

개인적으로 의존성 주입을 활용하지 않고 직접 Method를 호출하는것이
썩 내키지는 않았다.
또한 Method의 이름이 변경되면 해당 코드도 같이 변경되어야 한다는 것이
거슬렸다.

2) Qualifier

다음은 Qualifier를 사용하여 Bean 이 주입될때 구분지어 주는 것이다.

	@Bean
    @Primary
    LettuceConnectionFactory connectionFactory1() {
        return createConnectionFactoryWith(0);
    }

    @Bean
    StringRedisTemplate redisTemplate1(RedisConnectionFactory connectionFactory) { 
        return new StringRedisTemplate(connectionFactory);
    }
    
	@Bean
    @Qualifier(2)
    LettuceConnectionFactory connectionFactory2() {
        return createConnectionFactoryWith(1);
    }
    
    @Bean
    StringRedisTemplate redisTemplate2(@Qualifier(2) RedisConnectionFactory connectionFactory) { 
        return new StringRedisTemplate(connectionFactory);
    }

connectionFactory1이 @Primary 어노테이션으로 인해 기본값이니
connectionFactory2가 필요한 경우에 사용하기 위해 @Qualifier 어노테이션으로 구분을 지어주면 된다.

4. 최종 코드

	@Value("${project.redis.host}")
    private String host;

    @Value("${project.redis.port}")
    private int port;

    @Value("${project.a.redis.index}")
    private int aIndex;

    @Value("${project.b.redis.index}")
    private int bIndex;

    @Bean
    @Primary
    LettuceConnectionFactory connectionFactory1() { return createConnectionFactoryWith(aIndex); }

    @Bean
    StringRedisTemplate redisTemplate1(RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }

    @Bean
    @Qualifier("2")
    LettuceConnectionFactory connectionFactory2() { return createConnectionFactoryWith(bIndex); }

    @Bean
    StringRedisTemplate redisTemplate2(@Qualifier("2") RedisConnectionFactory connectionFactory) {
        return new StringRedisTemplate(connectionFactory);
    }

    public LettuceConnectionFactory createConnectionFactoryWith(int index) {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(host);
        redisStandaloneConfiguration.setPort(port);
        redisStandaloneConfiguration.setDatabase(index);

        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }

최종적으로 다음과 같은 코드가 완성되었다.
현재 같은 Redis 서버에 Database의 index만 다르기에
HostName, Port 에 대한것은 공통 설정으로 쓰고 index 정보만 매개변수로 받아
connectionFactory를 생성할 수 있는 메서드를 만들었다.
또한 각 파트마다 사용할 index 정보들도 상수로 Method로 집어 넣기 보다는
설정 정보이기에 application.properties 파일에서 설정해서 받아오도록 사용하였다.

최종적으로 0,1 번에 데이터가 잘 나눠져 들어가는걸 볼 수 있다.

profile
차근차근 꾸준히

0개의 댓글