ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ehcache 사용하기 (로컬 캐시 사용하기)
    극락코딩 2023. 8. 9. 13:44

    Spring Local Cache

    Local Cache를 통해 Network IO 비용을 줄이자

    로컬 캐시 description

    • 로컬 캐시는 스프링 init되고 나서, 서버의 로컬 캐싱 저장소에 데이터를 저장하는 방법을 의미
    • 변경이 없고, 빠르게 데이터를 서빙해야 하는 상황에서 해당 로컬 캐시 사용
    • 네트워크 부하가 많이 발생하는 상황이고, 조회되는 데이터가 변경되지 않는 다면 로컬 캐시 적용관련하여 고려 굳

    단점

    • 데이터 정합성이 중요한 경우, 로컬 캐시는 부적절함
    • 팟이 여러개 띄워진 상황에서 로컬캐시에 대한 cache evict을 어떻게 수행하지? -> 별도의 중간 관리체계가 필요함

    사용법

    Dependency 설정

    object DependencyVersion {
        const val EHCACHE_VERSION = "3.8.1"
        const val JAVA_CACHE_API_VERSION = "1.1.1"
    }
    
    implementation("org.springframework.boot:spring-boot-starter-cache")
    implementation("org.ehcache:ehcache:${DependencyVersion.EHCACHE_VERSION}")
    implementation("javax.cache:cache-api:${DependencyVersion.JAVA_CACHE_API_VERSION}")

    Ehcache.xml 설정

    Local cache를 등록할 데이터에 대한 설정을 진행한다. 어떤 타입인지, expired 시간을 어떻게 설정한 것인지 등에 대한 정보를 기입한다.

    
    <config
            xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
            xmlns='http://www.ehcache.org/v3'
            xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
            xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core.xsd
    http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
        <service>
            <jsr107:defaults enable-management="true" enable-statistics="true"/>
        </service>
    
        <!--여기부터 -->
        <cache-template name="myDefaultTemplate">
            <expiry>
                <ttl unit="minutes">60</ttl>
            </expiry>
            <listeners>
                <listener>
                    <class>com.goofy.localcache.config.EhCacheEventLogger</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>
            <heap>2</heap>
        </cache-template>
    
        <!--test-->
        <cache alias="Test.getTest" uses-template="myDefaultTemplate">
            <key-type>java.lang.Long</key-type>
            <value-type>java.lang.String</value-type>
            <resources>
                <heap>2</heap>
            </resources>
        </cache>
        <!--여기까지 사용자 환경설정 -->
    
    </config>

    cache config

    JCache에 대한 Config 설정을 진행한다. 기반은 ehcache.xml을 잡는다.

    @Configuration
    @EnableCaching
    class EhCacheConfiguration {
        companion object {
            private const val EHCACHE_XML_PATH = "ehcache.xml"
        }
    
        @Bean
        fun cacheManagerFactoryBean(): JCacheManagerFactoryBean {
            return JCacheManagerFactoryBean().apply {
                val path = ClassPathResource(EHCACHE_XML_PATH).uri
                this.setCacheManagerUri(path)
            }
        }
    
        @Bean
        fun testEhCacheManager(): CacheManager {
            return JCacheCacheManager().apply {
                val cacheManager = cacheManagerFactoryBean().getObject()
                this.cacheManager = cacheManager
            }
        }
    }

    ex cache service

    기존의 Spring Cache의 Cacheable과 사용법은 같다

    @Service
    class TestCacheService(
        private val testRepository: TestRepository
    ) {
        private val logger = KotlinLogging.logger {}
    
        @Cacheable(
            cacheManager = "testEhCacheManager",
            cacheNames = ["Test.getTest"],
            key = "#id"
        )
        fun getTest(id: Long): String? {
            return testRepository.findById(id)
        }
    }

    로컬 캐시를 등록하면, 현재 캐시가 없는 경우에는 캐시가 등록되고, 그 이후부터는 캐시 정보를 읽어서 진행한다.


    cache관련 작업이 수행된 경우 event를 발행하여 logging을 찍음

    기존의 xml에 event listener 추기

    <listeners>
        <listener>
            <class>com.goofy.localcache.config.EhCacheEventLogger</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>

    위의 내용을 xml에 추가한 이후, eventListener를 추가로 생성해야 함

    class EhCacheEventLogger : CacheEventListener<String, Any> {
        private val logger = KotlinLogging.logger {}
    
        override fun onEvent(cacheEvent: CacheEvent<out String, out Any>) {
            logger.info { "cache event logger key : ${cacheEvent.key} / oldValue : ${cacheEvent.oldValue} / newValue : ${cacheEvent.newValue}" }
        }
    }

    관련자료

    '극락코딩' 카테고리의 다른 글

    Kotlin에서 Builder Pattern을?  (0) 2023.08.15
    ThreadLocal을 간단하게 살펴보기  (0) 2023.08.15
    시멘틱 버저닝이란 무엇인가?  (0) 2023.08.09
    OLTP와 OLAP  (0) 2023.07.30
    Server Shutdown  (0) 2023.07.30
극락코딩