緩存管理器CacheManager使用

来源:https://www.cnblogs.com/checkcode/archive/2022/12/06/CacheManager.html
-Advertisement-
Play Games

緩存管理器CacheManager 一、背景 ​ 代碼併發量因建行活動頁上升,大量請求打到Mongo導致資料庫cpu100%從而服務不可用,目前解決方案,使用編程式緩存,即對緩存的操作與業務代碼耦合。目前基本上可以解決併發問題。此次提出CacheManager主要是優化代碼。使用聲明式,即註解的方式 ...


緩存管理器CacheManager

一、背景

​ 代碼併發量因建行活動頁上升,大量請求打到Mongo導致資料庫cpu100%從而服務不可用,目前解決方案,使用編程式緩存,即對緩存的操作與業務代碼耦合。目前基本上可以解決併發問題。此次提出CacheManager主要是優化代碼。使用聲明式,即註解的方式,靈活操縱緩存,不需要與業務代碼耦合。

二、與Springboot2集成

1、引入依賴

<!--SpringCache的依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2、SpringCache配置

/**
 * @Title: CacheManagerConfiguration
 * @author: simon
 * @date: 2021/12/27 14:14
 */
@EnableCaching
@Configuration
public class CacheManagerConfiguration extends CachingConfigurerSupport {

    private static String SEPARATOR = "&";

    @Autowired
    ReactiveMongoTemplate reactiveMongoTemplate;

    /**
     * 緩存管理器
     *
     * @param lettuceConnectionFactory
     * @return
     * @author simon
     */
    @Bean("redisCacheManager")
    @Primary
    public CacheManager cacheManager(@Qualifier("RedisConnectionFactory")
                                             LettuceConnectionFactory lettuceConnectionFactory)
    {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();

        // 解決查詢緩存轉換異常的問題
        ObjectMapper mapper = new ObjectMapper();

        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);

        //反序列化時候遇到不匹配的屬性並不拋出異常
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        //序列化時候遇到空對象不拋出異常
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        //反序列化的時候如果是無效子類型,不拋出異常
        mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
        //不使用預設的dateTime進行序列化,
        mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
        //使用JSR310提供的序列化類,裡面包含了大量的JDK8時間序列化類
        mapper.registerModule(new JavaTimeModule());
        //啟用反序列化所需的類型信息,在屬性中添加@class
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);


        RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer(mapper);
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonSerializer);

        // 初始化一個RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(lettuceConnectionFactory);
        // 預設緩存配置
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .computePrefixWith(comPrefix -> RedisConstants.KEY_BASE_PRIMITIVE.concat(RedisConstants.KEY_SEPARATOR)
                        .concat("cacheManager")
                        .concat(RedisConstants.KEY_SEPARATOR)
                        .concat(comPrefix)
                        .concat(RedisConstants.KEY_SEPARATOR))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(pair);
        // 初始化緩存key
        Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>(16);
        configurationMap.put(CacheDurationConstant.FOREVER_CACHE, defaultCacheConfig);
        configurationMap.put(CacheDurationConstant.THREE_DAYS_CACHE, defaultCacheConfig.entryTtl(Duration.ofDays(3)));
        configurationMap.put(CacheDurationConstant.ONE_DAYS_CACHE, defaultCacheConfig.entryTtl(Duration.ofDays(1)));
        configurationMap.put(CacheDurationConstant.ONE_HOURS_CACHE, defaultCacheConfig.entryTtl(Duration.ofHours(1)));
        configurationMap.put(CacheDurationConstant.TWO_MINUTES_CACHE, defaultCacheConfig.entryTtl(Duration.ofMinutes(2)));
        configurationMap.put(CacheDurationConstant.FIVE_SECONDS_CACHE, defaultCacheConfig.entryTtl(Duration.ofSeconds(5)));

        return RedisCacheManager.builder(redisCacheWriter)
                // 初始化緩存空間
                .initialCacheNames(configurationMap.keySet())
                // 初始化緩存配置
                .withInitialCacheConfigurations(configurationMap)
                // 預設緩存配置
                .cacheDefaults(defaultCacheConfig)
                .build();
    }

    @Bean
    @Primary
    @Override //繼承上面這個類,並且加上這個之後才能把它設置為預設的。
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            String className = target.getClass().getSimpleName();
            String methodName = method.getName();
            StringBuilder paramBuilder = new StringBuilder();
            if (params.length > 0) {
                Object param = params[0];
                // 參數為map自定義key=類名+方法名+map的key-value值
                if (param instanceof Map) {
                    Map<String, Object> map = (Map<String, Object>) param;
                    if (!map.isEmpty()) {
                        for (String key : map.keySet()) {
                            paramBuilder.append(key).append("-").append(map.get(key)).append(CacheManagerConfiguration.SEPARATOR);
                        }
                    }
                } else {
                    for (Object key : params) {
                        if (key != null) {
                            paramBuilder.append(JSONObject.toJSONString(key)).append(CacheManagerConfiguration.SEPARATOR);
                        }
                    }
                }
            }
            String paramString = paramBuilder.toString().replaceAll(":", ":");
            if (paramString.endsWith(CacheManagerConfiguration.SEPARATOR)) {
                paramString = paramString.substring(0, paramString.length() - 1);
            }
            SimpleKey key = new SimpleKey(className, methodName, paramString);
            return key;
        };
    }
}

詳情請看附件

註:註意不要將ObjectMapper加入到Spring容器中,因為Spring容器中存在一個ObjectMapper,以用於@RequestBodyResponseBodyRestTemplate等地的序列化和反序列化。

為什麼不採用Spring容器的ObjectMapper對象,而要自己設置是因為Redis配置了objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);屬性,在序列化時記錄類/屬性的類型,以便在反序列化時得到POJO對象。

三、使用

1、基於聲明式註釋的緩存

SpringCache是Service層的聲明式緩存。即無需與業務代碼耦合,通過註解完成緩存。

註解通用屬性:

屬性/方法名 解釋
value 緩存名,必填,它指定了你的緩存存放在哪塊命名空間
cacheNames 與 value 差不多,二選一即可
key 可選屬性,可以使用 SpEL 標簽自定義緩存的key
keyGenerator key的生成器。key/keyGenerator二選一使用
cacheManager 指定緩存管理器
cacheResolver 指定獲取解析器
condition 條件符合則緩存
unless 條件符合則不緩存
1.1、@Cacheable註解

根據方法對其返回結果進行緩存,下次請求時,如果緩存存在,則直接讀取緩存數據返回;如果緩存不存在,則執行方法,並把返回的結果存入緩存中。一般用在查詢方法上。 查看源碼,特殊屬性值如下:

屬性/方法名 解釋
sync 是否使用非同步模式,預設為false

附:SpringCache的元數據,可用在Key上

名稱 位置 描述 例子
methodMame root 被調用方法的名稱 #root.methodName
method root 被調用的方法 #root.method.name
target root 被調用的目標對象 #root.target
targetClass root 被調用目標的類 #root.targetClass
args root 用於被調用目標的參數值(數組) #root.args[0]
caches root 執行當前方法緩存的集合 #root.caches[0].name
參數名稱 調用的方法 方法的任何參數名稱 #iban或#a0
result 調用的方法 僅用在unless,方法調用的結果(緩存值) #result
1.2、@CachePut註解

使用該註解標誌的方法,每次都會執行,並將結果存入指定的緩存中。其他方法可以直接從響應的緩存中讀取緩存數據,而不需要再去查詢資料庫。一般用在新增方法上

1.3、@CacheEvict註解

使用該註解標誌的方法,會清空指定的緩存。一般用在更新或者刪除方法上 查看源碼,特殊屬性值如下:

屬性/方法名 解釋
allEntries 該參數指示是否需要在整個緩存範圍內逐出而不僅僅是基於Key的逐條逐出,預設為 false。如果指定為 true,則方法調用後將立即清空所有的緩存
beforeInvocation 是否在方法執行前就清空,預設為 false。如果指定為 true,則在方法執行前就會清空緩存
1.4、@Caching註解

該註解可以實現同一個方法上同時使用多種註解。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 在使用mybatis框架進行開發時,編寫sql少不了<=,>=,>,<,<>等比較符號,但是直接在mapper文件中,直接使用這些符號是不行的,此時就需要對類似於這種的符號進行轉換。 正文 直接符號轉義 以下羅列出sql中的比較符合和對應的轉義符號: 大於 > ==> &gt; 大於等於 >= ...
  • 本文主要介紹方法引用的5種形式:對象的非靜態方法引用、類的靜態方法引用、類的非靜態方法引用、構造器的引用、數組的引用。 ...
  • 有來實驗室結合正式的商城訂單支付業務場景將 Seata 分散式事務可視化,通過現象去看本質(原理和源碼),告別被動式輸入的短期記憶學習。 ...
  • C# 簡介 C#是微軟公司發佈的一種由C和C++衍生出來的面向對象的編程語言,它不僅去掉了 C++ 和 Java 語言中的一些複雜特性,還提供了可視化工具,能夠高效地編寫程式。 C#是由C和C++衍生出來的一種安全的、穩定的、簡單的、優雅的面向對象編程語言。它在繼承C和C++強大功能的同時去掉了一些 ...
  • 防禦式編程的重點就是需要防禦一些程式未曾預料的錯誤,這是一種提高軟體質量的輔助性方法,斷言assert就用於防禦式編程,編寫代碼時,我們總是會做出一些假設,斷言就是用於在代碼中捕捉這些假設。使用斷言是為了驗證預期的結果——當程式執行到斷言的位置時,對應的斷言應該為真;若斷言不為真時,程式會終止執行, ...
  • 十七、插入數據 本章將介紹如何利用sql的INSERT語句將數據插入表中 數據插入 插入分為以下幾種方式:插入完整的行、插入行的一部分、插入多行、插入某些查詢結果 插入完整的行 INSERT INTO Customers VALUES(NULL, 'Pep E. LaPew', '100 Main ...
  • 1.5 HDFS分散式文件系統 1.5.1 HDFS 簡介 HDFS(全稱:Hadoop Distribute File System,Hadoop 分散式文件系統)是 Hadoop 核心組成,是分散式存儲服務。 分散式文件系統橫跨多台電腦,在大數據時代有著廣泛的應用前景,它們為存儲和處理超大規模 ...
  • sysbench是一個開源的、基於LuaJIT(LuaJIT 是 Lua 的即時編譯器,可將代碼直接翻譯成機器碼,性能比原生 lua 要高) 的、可自定義腳本的多線程基準測試工具,也是目前用得最多的 MySQL 性能壓測工具。 基於 sysbench,我們可以對比 MySQL 在不同版本、不同硬體配 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...