
Redis는 한 서버에 0-15번 까지 총 16개의 Database를 가질 수 있다.
(http://redisgate.kr/redis/configuration/param_databases.php)
현재 개발중인 프로젝트에서 0,1 번 2개의 Database를 사용하고자 했다.
@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);
}

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

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

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

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

어디갔나 했더니 0번 데이터베이스에 사이 좋게 들어가 있었다...
@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와 연결되어 있는 것이였다.
4번째 log를 보면 직접 Method를 호출하는 경우에는 우리가 원하는 connectionFactory2의 객체를 받을 수 있다.
그래서 다음과 같이 수정하면 우리가 원하는 대로 redisTemplate2를 생성할때
connectionFactory2 객체를 매개변수로 전달할 수 있다.
@Bean
StringRedisTemplate redisTemplate1() {
return new StringRedisTemplate(connectionFactory1());
}
@Bean
StringRedisTemplate redisTemplate2() {
return new StringRedisTemplate(connectionFactory2());
}
외부에서 의존성을 주입 받기 보다는 원하는 객체를 사용하기 위해
우리가 직접 Method 를 Call 하는 것이다.
개인적으로 의존성 주입을 활용하지 않고 직접 Method를 호출하는것이
썩 내키지는 않았다.
또한 Method의 이름이 변경되면 해당 코드도 같이 변경되어야 한다는 것이
거슬렸다.
다음은 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 어노테이션으로 구분을 지어주면 된다.
@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 번에 데이터가 잘 나눠져 들어가는걸 볼 수 있다.