본문 바로가기

서버/SpringBoot

[Spring] EHcache를 이용해 캐시 간단하게 적용하기 (feat. @ Cacheable)

자주 바뀌는 값이 아닌데, 데이터베이스에 접근해서 가져오는데 시간이 걸려서 불편할 때가 있다. 이럴 때 간단하게 서버 단에서 캐시로 들고 있을 수 있다. 

필요성 

  • 데이터 중 매번 실시간 데이터를 반드시 반영해야 하는 경우가 아닐 때
  • 복잡한 쿼리라 가져오는 작업이 빠르지 않을 때
  • 위와 같은 경우 캐시 라이브러리를 이용해서 관리하면 성능에 도움이 된다. 
  • 스프링의 어노테이션을 이용해서 쉽게 캐싱처리를 할 수 있다. 

EHcache

  • 자바 기반 캐시 오픈소스 
  • 기본 JVM메모리에 저장된다. 
  • 레디스처럼 별도의 서버 설치 없이 사용할 수 있어, 가볍게 사용하기 좋다. 
  • EHcache는 로컬캐시로서 특정 서버에 종속된다. → 따라서 멀티 서버 환경에서는 데이터 싱크가 필요하다. 
  • 장 : 데이터 접근이 빠르다. 
  • 단 : 동기화가 속도에 영향을 줄 수 있고, 데이터 유실 가능성이 있다. 

초기 설정 

  1. dependency추가
    •  
    • implementation "org.springframework.boot:spring-boot-starter-cache" implementation "org.ehcache:ehcache:3.8.0" implementation "javax.cache:cache-api:1.0.0"
  2. Application에 어노테이션 추가 
    • @EnableCaching
      @EnableScheduling
      @SpringBootApplication
      public class CubeApplication {
      
        public static void main(String[] args) {
          SpringApplication.run(CubeApplication.class, args);
        }
      }

ehcache.xml 설정

https://www.ehcache.org/documentation/3.3/getting-started.html

  • 하나의 캐시를 사용할 때마다 <cache>를 구현해야 한다. 
<cache alias="optionLists"> <!-- 캐시의 이름을 지정 -->
  <key-type>java.lang.Object</key-type> <!-- Cache 인스턴스에 저장될 캐시의 키의 FQCN을 지정. 기본 값은 java.lang.Object -->
  <value-type>java.lang.Object</value-type> <!-- value-type 요소는 Cache 인스턴스에 저장된 값의 FQCN을 지정. 기본 값은 java.lang.Object -->
  <expiry> <!-- 캐시 만료기간 -->
    <ttl unit="hours">12</ttl> <!-- 12시간이 지나면 만료되도록 설정함 -->
    <!-- unit : days, hours, minutes, seconds, millis, micros, nanos -->
  </expiry>

<!-- 캐시가 생성되고 삭제되는 이벤트를 모니터링 하고 싶을 때의 설정 -->
  <listeners>
    <listener>
      <class>com.kakaopay.cube.common.CacheEventLogger</class>
      <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
      <event-ordering-mode>UNORDERED</event-ordering-mode>
      <events-to-fire-on>CREATED</events-to-fire-on>
      <events-to-fire-on>EXPIRED</events-to-fire-on>
    </listener>
  </listeners>

  <resources> <!-- 캐시 데이터의 저장 공간과 용량을 지정  -->
    <heap unit="entries">2</heap> <!-- 2개의 캐시를 사용하도록 설정 -->
  </resources>
</cache>

 <resources>

  • resources는 캐시 데이터의 저장 공간과 용량을 지정한다. 
  • <heap> : JVM 힙 메모리에 캐시 저장하도록 세팅 
  • <offheap> : JVM 힙 메모리 외부의 메모리에 캐시를 저장하도록 세팅 
  • 옵션
    • unit="entries" : 개수 지정 
    • unit="MB" : 용량 지정

캐시의 개수나 용량을 늘리면 

  • 파라미터가 없으면 그냥 하나의 캐시에 하나만 저장하면 된다. → entries = 1
  • 파라미터가 있으면 캐시 하나로 부족
  • 캐시 개수나 용량을 늘리면 그거에 맞게 여러개의 파라미터에 대한 결과값이 저장된다.

이벤트 모니터링 

  • ehcache.xml에서 모니터링 설정을 했다면 아래 처럼 CacheEventLogger를 선언해서 사용할 수 있다 .
  • 이때 CacheEventLogger는 <listeners> 내부 <class>로 설정된 위치에 있어야 한다. 
@Slf4j
public class CacheEventLogger implements CacheEventListener<Object, Object> {

  @Override
  public void onEvent(CacheEvent<? extends Object, ? extends Object> cacheEvent) {
    log.info("cache event logger message:::::: getKey: {} / getOldValue: {} / getNewValue:{}",
        cacheEvent.getKey(), cacheEvent.getOldValue(), cacheEvent.getNewValue());
  }
}

캐싱 처리 

  • 캐싱 처리를 하고 싶은 메소드 위에 @Cacheable만 설정해주면 된다.
  • 이때, @Cacheable(cacheNames=캐시이름)에서 캐시이름은 ehcache.xml에서 alias로 설정한 이름 
  • @Cacheable는 캐시 저장 및 조회를 위한 어노테이션으로 처음 호출 됐을 때는 메소드 내부를 실행하고, 두 번째 부터는 메소드 내부를 실행하지 않고 캐시에서 값을 가져온다. 
@Cacheable(cacheNames = "optionLists")
public String getOptionLists() {
}

캐시 저장 및 제거 스케줄링

  • @Scheduled 어노테이션을 이용해서 캐시를 저장하고 제거하는 메소드를 스케줄링 할 수 있다. 
  • @Scheduled(cron = "0 50 7 * * *")
    public void deleteAllCache() {
      for (String cacheName : cacheManager.getCacheNames()) {
        Objects.requireNonNull(Objects.requireNonNull(cacheManager.getCache(cacheName))).clear();
      }
    }
    
    @Scheduled(cron = "0 0 8 * * *")
    public void saveUserCountCache() {
      impalaAuProfileRepository.getTotalAu(format.format(new Date()));
      impalaAuProfileRepository.getTotalUser(format.format(new Date()));
    }
반응형