- Today
- Total
개성있는 개발자 되기
RedisTemplate 을 이용해서 Multi Pojo get/set 할 때 이슈사항 본문
Spring Boot에서 제공하는 RedisTemplate 을 이용하면 쉽게 Redis 데이터를 Get/Set 할 수 있다.
이 때, Data를 Serialize 하는 방법을 설정할 수 있는데 org.springframework.data.redis.serializer 패키지에서 확인할 수 있다.
다양한 모델의 데이터를 Redis에서 조회해올 때 겪었던 이슈사항과 최종적으로 택한 방법을 정리하고자 한다.
아래 내용은 나와 정말 똑같은 고민을 가진 사람이 쓴 글이다 ㅋㅋ
1. GenericJackson2JsonRedisSerializer
첫번째로 시도했던 Serializer이다. 이 Serializer은 별도로 Class Type을 지정해줄 필요없이 자동으로 Object를 Json으로 직렬화 해주는게 장점이다.
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(jedisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
그러나 치명적인 단점이 있었다.
바로, Object의 Class Type을 함께 레디스에 넣는다는 것이다.
"{\"@class\":\"com.prnv.model.WhitePaper\",\"title\":\"Hey\",\"author\":{\"@class\":\"com.prnv.model.Author\",\"name\":\"Hello\"},\"description\":\"Description\"}"
위와 같이 @class 로, 이 Dto의 패키지까지 모두 포함되어 저장이 된다.
이게 왜 치명적이냐 함은, 이 데이터를 꺼내올때 (GET) @class로 지정된 동일한 패키지에 있는 Dto로 밖에 가져올 수 없다는 것이다.
이런식으로 데이터가 저장된다면, 다른 Application에서는 무조건 저 Class를 생성하고, 무조건 저 루트로 같은 이름으로 Object를 생성해 놔야 한다.
MSA 프로젝트란 무엇인가. 여러 Application API 들이 서로 상호작용하는게 기본이다. 이렇게 데이터가 저장된다면 MSA API 들은 Data Class Type 에 묶여버리게 된다.
2. Jackson2JsonRedisSerializer
위의 Generic Serializer를 버리고 Jackson2JsonRedisSerializer 이 Serializer를 택했다.
이 Serializer은 Class Type 형을 명시한다. 위와 같이 @class 를 포함해서 저장하지 않는다.
단점은, 항상 Class Type을 지정해줘야 한다는 것이다.
하지만 이부분은 메소드를 공통화 시키면 쉽게 풀리는 일이었다.
public boolean setHashData(String key, String hashKey, Object value) throws Exception {
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer(value.getClass()));
redisTemplate.opsForHash().put(key, hashKey, value);
return true;
}
위와 같이 저장하고자하는 PoJo Type을 파라미터로 받아서, Jackson2JsonRedisSerializer를 생성한다.
문제가 해결된듯 보였다. 하지만 또 다른 문제가 발생한다.
바로, 이렇게 되면 각 Class Type은 따로따로 RedisTemplate을 생성해야 한다는 것이다.
Serializer는 redisTemplate에 설정하는 것이다. 그리고 이 redisTemplate은 (의도한것은) 모든 서비스에서 불러다 쓰는 공통 Service이다.
각 서비스들이 이 Resource 에 접근해서 Seriazlier를 바꿔치기 해버리면 어떻게 될까?
동기 호출이면 모를까, 여러 API들이 호출될텐데, 각 스레드들이 redisTemplate에 접근해서 데이터를 Get 하다가 Serializer가 바꿔치기 되버려서 결국 Serialize 에러가 나버린다.
대안은 두가지였다.
1. 각 Dto를 가지는 API 들이 Redis 에서 Get/Set할때 각자 고유한 RedisTemplate을 생성해서 가지고 있는다.
2. Generic으로 돌아와서, 모든 @class 타입을 맞춰버린다.
두 가지 다 너무 구렸다. 그래서 생각을 해낸게 JacksonSerializer를 쓰지 말고 StringSerializer를 쓰고 Json으로 파싱하는 것은 우리가 직접 하자!!
3. StringRedisSerializer
@Bean
public RedisTemplate<String, Object> redisTemplateTest() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
/**
* Redis 데이터 조회
* @param <T>
* @param key
* @param classType
* @return
* @throws Exception
*/
public <T> T getData(String key, Class<T> classType) throws Exception {
String jsonResult = (String) redisTemplate.opsForValue().get(key);
if (StringUtils.isBlank(jsonResult)) {
return null;
} else {
ObjectMapper mapper = new ObjectMapper();
T obj = mapper.readValue(jsonResult, classType);
return obj;
}
}
깔-끔
Spring 에서 제공하는 RedisTemplate 메소드가 Develop 되면 모를까.. 영문 자료밖에 없고 힘들었지만, 나름 뿌듯하네
참고로, RedisTemplate 하나만으로 여러 스레드들이 작동할 수 있는것은 Lettuce가 멀티 스레드를 지원하기 때문이다.
자세한 내용은 아래 공식문서를 보자
https://docs.spring.io/spring-data/redis/docs/current/reference/html/#redis:serializer
'Open Source > Redis' 카테고리의 다른 글
Redis-Exporter에서 표시되는 Graph 수치 (0) | 2020.03.19 |
---|---|
Redis Exporter 프로메테우스 Metrics (0) | 2020.03.19 |
Redis-cli 사용법 (1) | 2020.03.19 |