728x90
기존의 Spring에서도 공통코드, 메뉴코드와 같이 데이터 변경이 잘없는 호출은 SpringCache를 활용하였다. 이 컨셉을 계승하여 MSA에 적용하는 사례를 소개코자 한다. 그리고 Redis Cache로 외부 Cache 저장소를 사용할 것이다.
Github 소스코드
https://github.com/armyost/ApiRedisTemplate
GitHub - armyost/ApiRedisTemplate: This is api call template with redis cache
This is api call template with redis cache. Contribute to armyost/ApiRedisTemplate development by creating an account on GitHub.
github.com
Application.yml
spring:
cache:
type: redis
redis:
host: redis01.armyost.com
port: 6379
RestApiTemplate.java (코드에는 DemoService 라고 되어있음)
아래는 Rest API 호출시 사용할 Template을 공통기능으로 만든것이다. 업무에서 규격에 맞춰 사용하면 된다.
아래 Cacheable에서 cacheManager가 contentCacheManager로 되어 있다. Redis config시 관련 CacheManager를 등록해주자.
@Service
public class DemoService {
private static final Logger logger = LoggerFactory.getLogger(DemoService.class);
// You can set URL for cache.
@Cacheable(value = "myAwesomeCache", cacheManager = "contentCacheManager", condition = "#url.startsWith('/realApplication/')", key = "#url + '::' + #param")
public Map getRestData(String host, String url, Map param, HttpMethod method) {
return getRestDataWithSession(host, url, param, method, null);
}
public Map getRestDataWithSession(String host, String url, Map param, HttpMethod method, String sessionId) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
if (sessionId != null) {
headers.add(headers.COOKIE, "JSESSIONID=" + sessionId);
}
headers.setAccept(Arrays.asList(new MediaType[] { MediaType.APPLICATION_JSON }));
String fullUri = host + url;
String jsonParam = null;
if (method.equals(HttpMethod.GET) || method.equals(HttpMethod.DELETE)) {
fullUri += "?" + ofFromDataStr(param, true);
} else {
headers.setContentType(MediaType.APPLICATION_JSON);
try {
jsonParam = mapToJsonString(param);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
HttpEntity request = new HttpEntity(jsonParam, headers);
ResponseEntity<String> response = restTemplate.exchange(fullUri, method, request, String.class);
Map resultMap = null;
try {
resultMap = jsonToMap(response.getBody());
} catch (IOException e) {
logger.error("!!! getRestData {} !!!", e.getMessage());
}
return resultMap;
}
private static String ofFromDataStr(Map<Object, Object> data, boolean needEncode) {
if (data == null) {
return "";
}
StringBuilder builder = new StringBuilder();
for (Map.Entry<Object, Object> entry : data.entrySet()) {
if (builder.length() > 0) {
builder.append("&");
}
builder.append(URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8));
builder.append("=");
if (needEncode) {
builder.append(URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8));
} else {
builder.append(entry.getValue().toString());
}
}
return builder.toString();
}
private static TreeMap jsonToMap(String json) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
TreeMap<String, ArrayList<TreeMap>> map = mapper.readValue(json, TreeMap.class);
return map;
}
private static String mapToJsonString(Map param) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(param);
}
private static Map listToMap(List<TreeMap> list, String key, String value) {
Map retMap = new TreeMap();
for (Map map : list) {
String kValue = (String) map.get(key);
String vValue = (String) map.get(value);
retMap.put(kValue, vValue);
}
return retMap;
}
}
RedisConfig.java
@EnableCaching
@Configuration
public class RedisConfig {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}
@SuppressWarnings("deprecation")
@Bean
public CacheManager contentCacheManager() {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder
.fromConnectionFactory(redisConnectionFactory());
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer())) // Value Serializer 변경
.prefixKeysWith("Sample:") // Key Prefix로 "Sample:"를 앞에 붙여 저장
.entryTtl(Duration.ofMinutes(30)); // 캐시 수명 30분
builder.cacheDefaults(configuration);
return builder.build();
}
}
※ 실제로는 Cache 되는 시간도 다양한 옵션을 제공할 수 있도록, 여러 메서드를 시간별로 등록하여 사용하는 것이 더 좋을듯 하다.
'Programming > 기타' 카테고리의 다른 글
SQL Injection 에 대한 대응 (0) | 2023.10.13 |
---|---|
Refresh Token에 대한 고찰 (0) | 2023.09.25 |
코드 품질올리기, 코드 설계 - 3 성숙한 클래스 만들기 (3/3) (0) | 2023.08.30 |
코드 품질올리기, 코드 설계 - 3 성숙한 클래스 만들기 (2/3) (0) | 2023.08.30 |
코드 품질올리기, 코드 설계 - 3 성숙한 클래스 만들기 (1/3) (0) | 2023.08.30 |