2024.05.08 - [Spring/대용량 트래픽] - Spring Boot Cache
이전글에서는 RedisTemplate과 @RedisHash를 이용해서 Redis를 캐시처럼 이용하는 연습을 했다.
이번에는 Spring 자체에서 제공하는 Spring cache를 사용해볼것이다.
우선 build.gradle에 의존성이 필요하다.
https://docs.spring.io/spring-framework/reference/integration/cache.html
자세한건 공식문서에 있으니 참고하면 좋을것 같다.
implementation 'org.springframework.boot:spring-boot-starter-cache'
스프링 캐시는 다양한 Provider를 제공하는데, Provider에 따라서 로컬 캐시로 쓸 것인지, 외부에서 동작하는 Redis캐시로 쓸것인지 결정이 가능하다. 물론 모두 활용하는것도 가능하다.
사용법
- Application.java에 @EnableCaching 어노테이션을 추가한다. (or Config.java도 설정가능)
- cache를 활용하는 메서드에 @Cacheable 어노테이션을 추가한다.
- 어노테이션의 인자와, 메서드의 인자를 활용해서 Key를 만들고, 메서드 응답값을 Value로 캐시에 저장한다.
- @CacheEvict(cacheNames = "", key = "") : 저장된 캐시를 즉각 삭제한다.
정말 간단하다!
실습
먼저 Config 파일 설정이 필요하다.
CacheConfig.java
@EnableCaching
@Configuration
public class CacheConfig {
public static final String CACHE1 = "cache1";
public static final String CACHE2 = "cache2";
@AllArgsConstructor
@Getter
public static class CacheProperty{
private String name;
private Integer ttl;
}
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer(){
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator
.builder()
.allowIfSubType(Object.class)
.build();
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL)
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS);
List<CacheProperty> properties = List.of(
new CacheProperty(CACHE1, 300),
new CacheProperty(CACHE2, 30)
);
return (builder -> {
properties.forEach(i -> {
builder.withCacheConfiguration(i.getName(), RedisCacheConfiguration
.defaultCacheConfig()
.disableCachingNullValues()
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)))
.entryTtl(Duration.ofSeconds(i.getTtl()))
);
});
});
}
}
- Inner class 선언 : Cache의 정보를 담기위해 선언한다.
- @EnableCaching : Cache로 활용한다는 것을 스프링 프레임워크에 알린다.
- ObjectMapper : 이전글에서 사용했던 ObjectMapper와 같다. 모든 클래스형식에 대해서 직렬화하기 위해 필요하다.
- RedisCacheManagerBuilderCustomizer : 이전글에서 RedisTemplate객체와 달리, Config파일에서 Cache에 필요한 정보들을 세팅해준다.
이와 같이 전보다 더 편리하고 추상화된 기능을 제공해준다!
성능측정
이때까지 Redis를 캐시로 사용하기 위해서 알아봤는데, 실제로 성능이 좋아야 사용할 이유가 있을 것이다.
이에대해 테스트를 진행한다.
대량의 트래픽을 짧게 요청받기위해 부하테스트 툴인 vegeta 라는 오픈소스를 활용한다.
https://github.com/tsenart/vegeta
윈도우에서 설치하는 방법이 잘안나와있어서 알아보니 wsl에서 자신의 우분투를 들어간다.
sudo snap install vegeta
이 명령어를 통해서 설치할수 있다.
이제 wsl상에서의 IP주소에서 내가 사용하는 Windows상의 로컬주소로 요청을 보낼 것이므로, wsl의 IP주소를 알아야한다.
명령프롬프트에서 다음 명령을 내려서 현재 사용하고 있는 로컬 IP주소를 알수 있다.
ipconfig
나의 경우는 192.168.176.1 이다.
주소를 알아냈다면 우분투로 돌아가서 다음과 같이 요청파일을 만든다.
request1.txt
GET http://192.168.176.1:8080/users/1
GET http://192.168.176.1:8080/users/2
GET http://192.168.176.1:8080/users/3
파일을 만들고 요청 명령을 입력한다.
vegeta attack -timeout=30s -duration=15s -rate=5000/1s -targets=request1.txt -workers=100 | tee v_results.bin | vegeta report
- vegeta attack : 공격(트래픽)을 해라
- -timeout : 시간제한
- -duration : 트래픽을 거는 시간
- -rate : 트래픽 양
- -targets : 요청하고자하는 HTTP 파일
- -workers : 동시에 작동하는 worker 수
먼저 위에서 만들었던 @Cacheable을 주석처리해주고, DB에서 가져오는 처리 속도에 대해 테스트한다.
docker stats [ContainerID]
컨테이너 리소스를 체크해본다.
CPU 사용량이 변화하는 걸 볼수 있고, mysql에서 요청을 처리하지 못하는 것도 볼 수 있다.
성공률이 49.33%에 안된다,,
어노테이션을 다시 적용해서 캐시를 사용한다면 어떻게 될까?
엄청난 성능차이를 보여준다,,,
물론 실행과정중이어서 캡처하지를 못했지만, 레디스의 cpu사용률이 급증하는것도 볼수 있었다.
이렇게 캐시를 사용하면 적은리소스를 가지고, 대량의 트래픽을 처리하는데 매우 효율적임을 확인하고, 공부했다!
2024.05.13 - [Spring/대용량 트래픽] - Spring Session
'Spring > 대용량 트래픽' 카테고리의 다른 글
Spring Boot Pub/Sub (0) | 2024.05.14 |
---|---|
Spring Session (0) | 2024.05.13 |
Spring Boot Cache (0) | 2024.05.08 |
Redis Cache로 실습하기 (0) | 2024.05.08 |
Redis Cache 이론 (0) | 2024.05.07 |