1. 「性能提升」擴展 Spring Cache 支持多級緩存

        共 3518字,需瀏覽 8分鐘

         ·

        2020-09-28 13:00

        為什么多級緩存

        緩存的引入是現(xiàn)在大部分系統(tǒng)所必須考慮的

        • redis 作為常用中間件,雖然我們一般業(yè)務(wù)系統(tǒng)(畢竟業(yè)務(wù)量有限)不會遇到如下圖 在隨著 data-size 的增大和數(shù)據(jù)結(jié)構(gòu)的復(fù)雜的造成性能下降,但網(wǎng)絡(luò) IO 消耗會成為整個調(diào)用鏈路中不可忽視的部分。尤其在 微服務(wù)架構(gòu)中,一次調(diào)用往往會涉及多次調(diào)用 例如pig oauth2.0 的 client 認證[1]
        • Caffeine 來自未來的本地內(nèi)存緩存,性能比如常見的內(nèi)存緩存實現(xiàn)性能高出不少詳細對比[2]。

        綜合所述:我們需要構(gòu)建 L1 Caffeine JVM 級別緩存 , L2 Redis 緩存。

        設(shè)計難點

        目前大部分應(yīng)用緩存都是基于 Spring Cache 實現(xiàn),基于注解(annotation)的緩存(cache)技術(shù),存在的問題如下:

        • Spring Cache 僅支持 單一的緩存來源,即:只能選擇 Redis 實現(xiàn)或者 Caffeine 實現(xiàn),并不能同時使用。
        • 數(shù)據(jù)一致性:各層緩存之間的數(shù)據(jù)一致性問題,如應(yīng)用層緩存和分布式緩存之前的數(shù)據(jù)一致性問題。
        • 緩存過期:Spring Cache 不支持主動的過期策略

        業(yè)務(wù)流程

        如何使用


          1. 引入依賴
        <dependency>
        ????<groupId>com.pig4cloud.plugingroupId>
        ????<artifactId>multilevel-cache-spring-boot-starterartifactId>
        ????<version>0.0.1version>
        dependency>

          1. 開啟緩存支持
        @EnableCaching
        public?class?App?{
        ?public?static?void?main(String[]?args)?{
        ??SpringApplication.run(App.class,?args);
        ?}
        }

          1. 目標接口聲明 Spring Cache 注解
        @Cacheable(value?=?"get",key?=?"#key")
        @GetMapping("/get")
        public?String?get(String?key){
        ????return?"success";
        }

        性能比較

        為保證性能 redis 在 127.0.0.1 環(huán)路安裝

        • OS: macOS Mojave
        • CPU: 2.3 GHz Intel Core i5
        • RAM: 8 GB 2133 MHz LPDDR3
        • JVM: corretto_11.jdk
        BenchmarkModeCntScoreUnits
        多級實現(xiàn)thrpt22716.074ops/s
        默認 redisthrpt21373.476ops/s

        代碼原理

          1. 自定義 CacheManager 多級緩存實現(xiàn)
        public?class?RedisCaffeineCacheManager?implements?CacheManager?{

        ?@Override
        ?public?Cache?getCache(String?name)?{
        ??Cache?cache?=?cacheMap.get(name);
        ??if?(cache?!=?null)?{
        ???return?cache;
        ??}
        ??cache?=?new?RedisCaffeineCache(name,?stringKeyRedisTemplate,?caffeineCache(),?cacheConfigProperties);
        ??Cache?oldCache?=?cacheMap.putIfAbsent(name,?cache);
        ??log.debug("create?cache?instance,?the?cache?name?is?:?{}",?name);
        ??return?oldCache?==?null???cache?:?oldCache;
        ?}
        }
        • 多級讀取、過期策略實現(xiàn)


        public?class?RedisCaffeineCache?extends?AbstractValueAdaptingCache?{
        ?protected?Object?lookup(Object?key)?{
        ??Object?cacheKey?=?getKey(key);

        ????//?1.?先調(diào)用?caffeine?查詢是否存在指定的值
        ??Object?value?=?caffeineCache.getIfPresent(key);
        ??if?(value?!=?null)?{
        ???log.debug("get?cache?from?caffeine,?the?key?is?:?{}",?cacheKey);
        ???return?value;
        ??}

        ????//?2.?調(diào)用?redis?查詢在指定的值
        ??value?=?stringKeyRedisTemplate.opsForValue().get(cacheKey);

        ??if?(value?!=?null)?{
        ???log.debug("get?cache?from?redis?and?put?in?caffeine,?the?key?is?:?{}",?cacheKey);
        ???caffeineCache.put(key,?value);
        ??}
        ??return?value;
        ?}
        }

          1. 過期策略,所有更新操作都基于 redis pub/sub 消息機制更新
        public?class?RedisCaffeineCache?extends?AbstractValueAdaptingCache?{
        ?@Override
        ?public?void?put(Object?key,?Object?value)?{
        ??push(new?CacheMessage(this.name,?key));
        ?}

        ?@Override
        ?public?ValueWrapper?putIfAbsent(Object?key,?Object?value)?{
        ????push(new?CacheMessage(this.name,?key));
        ?}

        ?@Override
        ?public?void?evict(Object?key)?{
        ??push(new?CacheMessage(this.name,?key));
        ?}

        ?@Override
        ?public?void?clear()?{
        ??push(new?CacheMessage(this.name,?null));
        ?}

        ?private?void?push(CacheMessage?message)?{
        ??stringKeyRedisTemplate.convertAndSend(topic,?message);
        ?}
        }

          1. MessageListener 刪除指定 Caffeine 的指定值
        public?class?CacheMessageListener?implements?MessageListener?{

        ?private?final?RedisTemplate?redisTemplate;

        ?private?final?RedisCaffeineCacheManager?redisCaffeineCacheManager;

        ?@Override
        ?public?void?onMessage(Message?message,?byte[]?pattern)?{
        ??CacheMessage?cacheMessage?=?(CacheMessage)?redisTemplate.getValueSerializer().deserialize(message.getBody());
        ????cacheMessage.getCacheName(),?cacheMessage.getKey());
        ??redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(),?cacheMessage.getKey());
        ?}
        }

        源碼地址

        https://github.com/pig-mesh/multilevel-cache-spring-boot-starter

        參考資料

        [1]

        pig oauth2.0 的 client 認證: https://gitee.com/log4j/pig

        [2]

        詳細對比: https://github.com/ben-manes/caffeine/wiki/Benchmarks


        瀏覽 75
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 自拍九九 | 操逼无码视频 | 日韩三级hd | 午夜男女影院 | 欧美xxxx吸乳 |