Today
-
Yesterday
-
Total
-
  • Spring - Redis - Cache
    카테고리 없음 2020. 8. 27. 11:15

    https://yonguri.tistory.com/82

     

    SpringBoot기반 Redis Cache 활용법

    우리가 서비스를 개발할 때 백앤드 영역에서 Cache를 적극적으로 사용하게 되면 생각했던것 보다 더 드라마틱한 서비스 성능 개선을 가져올 수 있다(고 생각한다). 반대로 용도에 맞는 않는 정보

    yonguri.tistory.com

    우리가 서비스를 개발할 때 백앤드 영역에서 Cache를 적극적으로 사용하게 되면 생각했던것 보다 더 드라마틱한 서비스 성능 개선을 가져올 수 있다(고 생각한다). 반대로 용도에 맞는 않는 정보나 서비스요청에  캐시를 남용하게 되면 서비스 신뢰도에 큰 문제가 생길 수 있는 위험성도 내포하고 있다.

    이번글은 SpringBoot 기반에서 Cache서버를 얼마나 쉽게 구성할 수 있는지에 대한 간단한 개요정도의 글이다.

    Cache? Cache를 왜 사용하는가?

    • Cache?
      • 한번 읽은(처리한) 데이타를 임시로 저장하고 필요에 따라 전송,갱신,삭제하는 기술로
        보통은 데이타의 보관장소로 서버의 메모리를 사용하는 경우가 많다
      • 그렇기 때문에 디스크에서 정보를 얻어오는 것보다 훨씬 빠른 I/O성능을 얻을 수 있으나 서버가 다운되거나 재부팅되는 경우 사라지는 성격의 휘발성을 가지고 있어
      • 영속적으로 보관할 수 없는, 말 그대로 임시적으로 보관하고 빠르게 그 정보에 접근하기 위한 용도로 사용해야 한다.

        (물론 정보의 성격에 따라 별도의 디스크백업 및 TTL등의 설정으로 영구보관이나 오랜기간 유지가 가능하다. 단 이런 설정들이 꼭 필요하다면 Cache를 적용하는게 맞는지 한 번도 타당성을 검토해 보는게 좋겠다)
    • Cache를 쓰는 목적은 단순하다.
      • 서버간 불필요한 트래픽을 줄일 수 있고,
      • 그로 인해 웹어플리케이션 서버의 부하 감소시키고,
      • 어플리케이션의 빠른 처리성능(조회)을 확보해서 궁극적으로 어플리케이션를 사용하는 고객에게 쾌적한 서비스경험을 제공하는 것이다.

    Cache의 대상이 되는 정보들

    1. 단순한, 또는 단순한 구조의 정보를 -> 정보의 단순성 
    2. 반복적으로 동일하게 제공해야 하거나 -> 빈번한 동일요청의  반복
    3. 정보의 변경주기가 빈번하지 않고, 단위처리 시간이 오래걸리는 정보이고 -> 높은 단위처리비용
    4. 정보의 최신화가 반드시 실시간으로 이뤄지지 않아도 서비스 품질에 영향을 거의 주지 않는 정보 

    더 많은 조건들이 있겠으나,
    저 조건들 중 2개이상 포함되는 성격의 서비스와 정보라면
    Cache를 적용하는 것을 적극적으로 고려해 보아도 큰 무리가 없을 것 같다.

    어떤 정보들을 Cache로 사용하나?

    • 포탈의 검색어
    • 쇼핑몰의 핫딜상품, 베스트셀러, 추천상품등
    • 상품의 카테고리와 카테고리별 등록상품 수
    • 방문자수, 조회수, 추천수
    • 1회성 인증정보 (SMS 본인인증정보, IP정보등)
    • 공지사항, Q&A

    따로 설명을 하지 않더라도 Cache의 대상이 되는 정보들의 내용을 대입해보면 최소 1가지 이상은 포함되는 정보들이다. 
    우리가 사용하는 웹서비스들은 저 정보 뿐만이 아니라 훨씬 더 많은 정보들을 Cache를 적용해서 제공하고 있다고 보면 된다.

    단적으로, 우리가 웹사이트에서 보는 대부분의 이미지는 다 캐싱된 이미지이다.(CDN의 가장 중요한 기능)


    Cache를 사용할때 주의해야 할 것

    • 캐싱할 정보의 선택 -> 제일 중요하겠다.
    • 캐싱할 정보의 유효기간 (TTL - Time To Live ) 설정
    • 캐싱한 정보의 갱신시점

    서비스를 설계할 때, 특히 백앤드의 경우 API 서비스의 기능설계단계에서 부터 Cache정책을 수립하는게 좋다
    어떤 정보를 Cache로 적용할까를 먼저 따져보고 그 정보들을 어떤 시점에 어떤 주기로 갱신, 삭제를 할 지에 대한 최소한의 '캐싱전략'을 세우는 것도 어플리케이션 설계에서 중요한 영역중의 하나이다.

    Java(로 쓰고 Spring으로 읽고, Springboot를 쓴다)에서 Redis를 사용하는 이유

    • 추상화된 API와 어노테이션을 제공
      • 어노테이션 사용만으로 일반 Service 메서드를 캐시 함수로 사용 가능
    • SpringBoot의 Auto Configuration 적용으로 Cache서버 설정이 간결
      • Springboot  Starter Kit을 기본으로 제공함
        • spring-boot-starter-data-redis

    스프링부트에서 공식지원(추상화API 제공)하는 Third-Party Cache 라이브러리

    • Redis
    • Caffeine
    • EhCache
    • Hazelcate
    • Infinispan
    • Guava -> springboot 2.0에서 삭제

    위에서 얘기한 추상화된 API는 이 라이브러리들 모두에 동일하게 사용가능하다. 
    이 글의 주제는 Redis기반 설정이니 Redis기준으로 설명한다. 사실 그 외의 라이브러들은 EhCache와 Hazelcate정도 이름만 들어봤고 나머지는 생소한 것들이라 잘 알지 못한다.

    설정하는 방법은 많이 복잡하거나 까다로울게 없다.

    1. 스프링부트 의존성 라이브러리 추가
    2. Redis 커넥션 정보 설정 
    3. SpringBoot 메인클래스에 '나 캐시 사용할래'  알려주기
    4. 사용할 Method ( controller 메서드 or service 메서드)에 어노테이션 달아주기 -> 캐시전략에서 정해진 '서비스&정보'
    5. 사용

    이 정도의 작업만 해주면 Springboot 어플리케이션에서 Redis를 기본적인 Cache 서버로 사용할 수 있다.

    Redis CacheServer설정

    의존성 라이브러리 추가 (Maven기준) - pom.xml에 추가

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>

    Redis 서버설정 - application.yml or application.properties 파일에 추가

    spring.cache.type=redis
    spring.redis.host=localhost
    spring.redis.port=6379

    SpringBoot에 캐시사용하겠다고 알려주기 - Springboot Main Application Class에 @EnableCaching 어노테이션 추가

    @EnableCaching
    @SpringBootApplication
    public class RedisCacheApplication {
     public static void main(String[] args) {
         SpringApplication.run(RedisCacheApplication.class, args);
     }
    }
    

     

    사용할 서비스 메서드에 어노테이션 달아주기

    • 주요 어노테이션
      • @Cacheable @CachePut -> 캐시등록
        • @Cacheble은 캐시가 있으면 캐시의 정보를 가져오고, 없으면 등록한다.
        • @CacehPut은 무조건 캐시에 저장한다.
      • @CacheEvict -> 캐시삭제

    이렇게 해 주면 우리가 Cache가 필요한 API나  서비스 메서드에 어노테이션을 붙여서 바로 사용할 수 있다.

    • 일반 메서드에 어노테이션만 붙여서 바로 사용, 별도의 Cache 메서드를 정의할 필요가 없음
    • @Controller 메서드에 적용하면 파라메터를 Redis의 키값으로 자동지정됨.
    • condition, unless 어노테이션 옵션으로 특정 조건에 따른 캐시적용여부 설정가능

     캐시등록 및 조회 - @Cacheable, @CachePut

    캐시 어노테이션의 key, condition, unless의 옵션인자들은 'SpEL'(Spring Expression Language)을 지원한다. 즉 캐시의 키 값과 조건들을 메서드의 인자객체와 반환객체의 항목들로 동적으로 구성할 수 있다는 의미이다.

    https://www.baeldung.com/spring-expression-language

     

    Spring Expression Language Guide | Baeldung

    This article explores Spring Expression Language (SpEL), a powerful expression language that supports querying and manipulating object graphs at runtime.

    www.baeldung.com

    유명한(?) 밸덩 아저씨가 SpEL에 대해서 예제를 통해 잘 설명해 놓은 글이니 참고하면 좋을 듯 하다.

    아래의 간단한 예제는 특정 ID의 포스트글을 조회하는 API인데 캐시를 적용하되,
    반환된 Post객체에 담긴 실제 값 중 `shares`의 값이 500보다 작지 않을 경우에만 캐시를 적용하도록 설정해 놓은 코드이다. 500보다 작으면 굳이 캐시에 저장하지 않고 서비스를 직접 호출해서 값을 반환하겠다는 의미이다.

    @Cacheable(value = "post-single", key = "#id", unless = "#result.shares < 500") @GetMapping("/{id}") public Post getPostByID(@PathVariable String id) throws PostNotFoundException { log.info("get post with id {}", id); return postService.getPostByID(id); }

     

    캐시삭제 - @CacheEvict@CacheEvict(value = "post-single", key = "#id") @DeleteMapping("/delete/{id}") public void deletePostByID(@PathVariable String id) throws PostNotFoundException { log.info("delete post with id {}", id); postService.deletePost(id); }

    이렇게 @Controller 메세드에 직접 @CacheEvict 어노테이션을 직접 적용하게 되면 특정 ID의 포스트글을 지우는 요청하나로 서비스에서 실제 데이타를 지우고 동시에 Key로 지정된 - 즉, API의 요청 파라메터 - ID로 저장되어 있는 Redis키를 삭제할 수 있게 된다.

    스프링부트에서 기본적인 Springboot-date-cache 설정을 통해 캐시를 쉽게 적용할 수 있는 방법을 간단히 살펴보았는데, 조금 더 디테일하게 Cache를 사용하고 싶다면, 별도의 cacheManager를 오버라이딩해서 사용하면 된다.

    • Redis key에 데이타를 저장할때와 꺼낼때 처리할 Serializer의 지정 ( 일반적으로 string or  json serializer를 사용)
    • Key의 TTL(Time to Live)의 설정
    • Key의 Prefix 설정

    이런 설정들을 임의로 하고 싶다면 별도의 Configuation Class를 만들어 사용하도록 하자.


    Redis Cache 사용자 구성

    • 캐시의 TTL 적용 및 임의시점의 캐시등록/삭제 처리 등의 기능을 사용하고 싶으면 별도의 Configuration 설정필요

    • Springboot에서 제공하는 CachingConfigure 인터페이스를 구현해 놓은 거의 추상클래스인 CachingConfigureSupport클래스를 상속받아 별도의 Configuration Class 파일 추가

    • cacheManager() 을 오버라이딩  -> caheManager에 필요한 속성값 설정 (Serializer, TTL, keyPrefix 등)

    • @Cacheabe,CachePut,CacheEvice 어노테이션을 사용할때 cacheManager를 지정 또는, 필요한 클래스에 cacheManager를 주입받아 인라인 방식으로 직접 사용하면 된다.

    RedisCache Configuration Class

    @Configuration @EnableCaching public class RedisCacheConfig extends CachingConfigurerSupport { ... @Bean @Override public CacheManager cacheManager() { RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory()); RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) .prefixKeysWith("imhere:") .entryTtl(Duration.ofHours(5L)); builder.cacheDefaults(configuration); return builder.build(); }

     

    캐시 어노테이션에 등록한 cacheManager 지정

    @Cacheable(value = "post-single", key = "#id", unless = "#result.shares < 500", cacheManager="cacheManager")
    @GetMapping("/{id}")
    public Post getPostByID(@PathVariable String id) throws PostNotFoundException {
        log.info("get post with id {}", id);
        return postService.getPostByID(id);
    }

     

    메서드내에 필요한 곳에 인라인으로 캐시 사용

    @Slf4j
    @RequestMapping(value = "${api.path.default}")
    @RestController
    public class ClassesController extends BaseController {
    
        @Autowired
        CacheManager cacheManager;
        
        @PostMapping("/academies/{academyId}/classes")
        @Transactional
        public ResponseEntity<ApiResponseDto> addClassInfo(@PathVariable Long academyId,
                                                           @Valid @RequestBody ClassesDto.ClassesInfo classesInfo) {
                                                           
            ...
            ...
        
            // 캐시 초기화
            cacheManager.getCache("common-classes").evict("academies:" + academyId + ":classes");
        
            ...
            ...
        
        }
    }    
        
    

     

    개요정도의 수준으로 SpringBoot Redis Cache설정 및 활용법에 대해서 살펴 보았는데, 라이트한 목적으로 사용하기에는 어느정도 도움이 될 꺼라고 생각한다.
    기회가 되면 cacheManager와 각각의 어노테이션에 대해서 좀 더 깊은 내용으로 알아볼까 한다.



    출처: https://yonguri.tistory.com/82 [대디장 일상 블로그]

    댓글

Designed by Tistory.