最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot Caffeine+Redisson配置二級(jí)緩存實(shí)踐

 更新時(shí)間:2026年05月06日 09:01:09   作者:酒醉的胡鐵  
文章介紹了兩級(jí)緩存架構(gòu)的必要性,詳細(xì)描述了使用Redission進(jìn)行SpringBoot緩存整合的方法,包括配置本地緩存、設(shè)置過(guò)期時(shí)間、開(kāi)啟緩存功能、解決key相同cacheNames不同的問(wèn)題以及修改自定義緩存管理器等內(nèi)容

問(wèn)題說(shuō)明

在高性能的服務(wù)架構(gòu)設(shè)計(jì)中,緩存是一個(gè)不可或缺的環(huán)節(jié)。在實(shí)際的項(xiàng)目中,我們通常會(huì)將一些熱點(diǎn)數(shù)據(jù)存儲(chǔ)到Redis或MemCache這類緩存中間件中,只有當(dāng)緩存的訪問(wèn)沒(méi)有命中時(shí)再查詢數(shù)據(jù)庫(kù)。在提升訪問(wèn)速度的同時(shí),也能降低數(shù)據(jù)庫(kù)的壓力。

隨著不斷的發(fā)展,這一架構(gòu)也產(chǎn)生了改進(jìn),在一些場(chǎng)景下可能單純使用Redis類的遠(yuǎn)程緩存已經(jīng)不夠了,還需要進(jìn)一步配合本地緩存使用,例如Guava cacheCaffeine,從而再次提升程序的響應(yīng)速度與服務(wù)性能。于是,就產(chǎn)生了使用本地緩存作為一級(jí)緩存,再加上遠(yuǎn)程緩存作為二級(jí)緩存的兩級(jí)緩存架構(gòu)。

準(zhǔn)備

集成Redission:SpringBoot 整合Redisson重寫(xiě)cacheName支持多參數(shù)

<!--caffeine-->
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

配置文件

增加本地緩存是為了解決高并發(fā)下頻繁查詢Redis的問(wèn)題,配置了expireAfterWrite最后一次寫(xiě)入或訪問(wèn)后經(jīng)過(guò)固定時(shí)間過(guò)期為30秒,例如一次訪問(wèn)一個(gè)頁(yè)面一直刷新,在30秒內(nèi)無(wú)論怎么刷新都會(huì)走本地緩存,而且就算在29秒重新獲取了緩存,也會(huì)重新計(jì)算30秒

@EnableCaching開(kāi)啟緩存功能,也可以加在啟動(dòng)類上

package com.example.redisson.config;

import com.example.redisson.manager.PlusSpringCacheManager;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

/**
 * 緩存配置
 *
 * @author Lion Li
 */
@Configuration
@EnableCaching
public class CacheConfig {

    /**
     * caffeine 本地緩存處理器
     */
    @Bean
    public Cache<Object, Object> caffeine() {
        return Caffeine.newBuilder()
            // 設(shè)置最后一次寫(xiě)入或訪問(wèn)后經(jīng)過(guò)固定時(shí)間過(guò)期
            .expireAfterWrite(30, TimeUnit.SECONDS)
            // 初始的緩存空間大小
            .initialCapacity(100)
            // 緩存的最大條數(shù)
            .maximumSize(1000)
            .build();
    }

    /**
     * 自定義緩存管理器 整合spring-cache
     */
    @Bean
    public CacheManager cacheManager() {
        return new PlusSpringCacheManager();
    }

}

裝飾器

  • put方法是cache使用的也就是Redission ,會(huì)自動(dòng)加上cacheNames
  • 而caffeine不使用cacheNames ,get方法的key是哪個(gè)就會(huì)緩存哪個(gè)key
  • 所以為了解決key相同,cacheNames 不同的問(wèn)題增加了getUniqueKey方法 getName( ) = cacheNames

一個(gè)CacheNams對(duì)應(yīng)一個(gè)Cache對(duì)象

package org.dromara.common.redis.manager;

import cn.hutool.core.lang.Console;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.cache.Cache;

import java.util.concurrent.Callable;

/**
 * Cache 裝飾器模式(用于擴(kuò)展 Caffeine 一級(jí)緩存)
 *
 * @author LionLi
 */
public class CaffeineCacheDecorator implements Cache {

    private static final com.github.benmanes.caffeine.cache.Cache<Object, Object>
            CAFFEINE = SpringUtils.getBean("caffeine");

    private final Cache cache;

    public CaffeineCacheDecorator(Cache cache) {
        this.cache = cache;
    }

    @Override
    public String getName() {
        return cache.getName();
    }

    @Override
    public Object getNativeCache() {
        return cache.getNativeCache();
    }

    public String getUniqueKey(Object key) {
        return cache.getName() + ":" + key;
    }

    @Override
    public ValueWrapper get(Object key) {
        Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key));
        Console.log("redisson caffeine -> key: " + getUniqueKey(key) + ",value:" + o);
        return (ValueWrapper) o;
    }

    @SuppressWarnings("unchecked")
    public <T> T get(Object key, Class<T> type) {
        Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type));
        Console.log("redisson caffeine -> key: " + getUniqueKey(key) + ",value:" + o);
        return (T) o;
    }

    @Override
    public void put(Object key, Object value) {
        cache.put(key, value);
    }

    public ValueWrapper putIfAbsent(Object key, Object value) {
        return cache.putIfAbsent(key, value);
    }

    @Override
    public void evict(Object key) {
        evictIfPresent(key);
    }

    public boolean evictIfPresent(Object key) {
        boolean b = cache.evictIfPresent(key);
        if (b) {
            CAFFEINE.invalidate(getUniqueKey(key));
        }
        return b;
    }

    @Override
    public void clear() {
        cache.clear();
    }

    public boolean invalidate() {
        return cache.invalidate();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T get(Object key, Callable<T> valueLoader) {
        Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader));
        Console.log("redisson caffeine -> key: " + getUniqueKey(key) + ",value:" + o);
        return (T) o;
    }

}

修改自定義PlusSpringCacheManager,注入裝飾器

使用

@Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null")
@GetMapping("/test1")
public R<String> test1(String key, String value) {
    System.out.println("test1-->調(diào)用方法體");
    return R.ok("操作成功", value);
}

因?yàn)?code>@Cacheable注解上來(lái)就會(huì)調(diào)用get方法,所以可以觸發(fā)本地緩存,@CachePut注解則不行

查看源碼可知,如果一級(jí)沒(méi)有獲取到,則獲取二級(jí)

緩存工具類

package com.example.redisson.utils;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.redisson.api.RMap;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;

import java.util.Set;

/**
 * 緩存操作工具類 {@link }
 *
 * @author Michelle.Chung
 * @date 2022/8/13
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked"})
public class CacheUtils {

    private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);

    /**
     * 獲取緩存組內(nèi)所有的KEY
     *
     * @param cacheNames 緩存組名稱
     */
    public static Set<Object> keys(String cacheNames) {
        RMap<Object, Object> rmap = (RMap<Object, Object>) CACHE_MANAGER.getCache(cacheNames).getNativeCache();
        return rmap.keySet();
    }

    /**
     * 獲取緩存值
     *
     * @param cacheNames 緩存組名稱
     * @param key        緩存key
     */
    public static <T> T get(String cacheNames, Object key) {
        Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);
        return wrapper != null ? (T) wrapper.get() : null;
    }

    /**
     * 保存緩存值
     *
     * @param cacheNames 緩存組名稱
     * @param key        緩存key
     * @param value      緩存值
     */
    public static void put(String cacheNames, Object key, Object value) {
        CACHE_MANAGER.getCache(cacheNames).put(key, value);
    }

    /**
     * 刪除緩存值
     *
     * @param cacheNames 緩存組名稱
     * @param key        緩存key
     */
    public static void evict(String cacheNames, Object key) {
        CACHE_MANAGER.getCache(cacheNames).evict(key);
    }

    /**
     * 清空緩存值
     *
     * @param cacheNames 緩存組名稱
     */
    public static void clear(String cacheNames) {
        CACHE_MANAGER.getCache(cacheNames).clear();
    }

}

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 一文帶你掌握J(rèn)ava?SPI的原理和實(shí)踐

    一文帶你掌握J(rèn)ava?SPI的原理和實(shí)踐

    在Java中,我們經(jīng)常會(huì)提到面向接口編程,這樣減少了模塊之間的耦合,更加靈活,Java?SPI?(Service?Provider?Interface)就提供了這樣的機(jī)制,本文就來(lái)講講它的原理與具體使用吧
    2023-05-05
  • spring mvc 實(shí)現(xiàn)獲取后端傳遞的值操作示例

    spring mvc 實(shí)現(xiàn)獲取后端傳遞的值操作示例

    這篇文章主要介紹了spring mvc 實(shí)現(xiàn)獲取后端傳遞的值操作,結(jié)合實(shí)例形式詳細(xì)分析了spring mvc使用JSTL 方法獲取后端傳遞的值相關(guān)操作技巧
    2019-11-11
  • Java并發(fā)編程Callable與Future的應(yīng)用實(shí)例代碼

    Java并發(fā)編程Callable與Future的應(yīng)用實(shí)例代碼

    這篇文章主要介紹了Java并發(fā)編程Callable與Future的應(yīng)用實(shí)例代碼,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • SpringBoot動(dòng)態(tài)修改yml配置文件的方法詳解

    SpringBoot動(dòng)態(tài)修改yml配置文件的方法詳解

    這篇文章主要為大家詳細(xì)介紹了SpringBoot動(dòng)態(tài)修改yml配置文件的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • mybatis配置獲取自增主鍵的操作方法

    mybatis配置獲取自增主鍵的操作方法

    當(dāng)需要?jiǎng)倓偛迦霐?shù)據(jù)庫(kù)的數(shù)據(jù)對(duì)應(yīng)的新增主鍵時(shí),通過(guò)配置xml文件,使數(shù)據(jù)庫(kù)返回新增主鍵id,并把主鍵id與類參數(shù)對(duì)應(yīng),本文給大家介紹了mybatis配置獲取自增主鍵的操作方法,需要的朋友可以參考下
    2024-05-05
  • java實(shí)現(xiàn)九宮格拼圖游戲

    java實(shí)現(xiàn)九宮格拼圖游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)九宮格拼圖游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Java中的XML解析技術(shù)詳析

    Java中的XML解析技術(shù)詳析

    XML文檔是一個(gè)文檔樹(shù),從根部開(kāi)始,并擴(kuò)展到樹(shù)的最底部,下面這篇文章主要給大家介紹了關(guān)于Java中XML解析技術(shù)的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-08-08
  • java實(shí)現(xiàn)支付寶退款功能

    java實(shí)現(xiàn)支付寶退款功能

    這篇文章主要為大家詳細(xì) 介紹了java實(shí)現(xiàn)支付寶退款功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • Java實(shí)題演練二叉搜索樹(shù)與雙向鏈表分析

    Java實(shí)題演練二叉搜索樹(shù)與雙向鏈表分析

    這篇文章主要介紹了Java二叉搜索樹(shù)與雙向鏈表,總的來(lái)說(shuō)這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達(dá)的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過(guò)程。希望通過(guò)這道題能給你帶來(lái)一種解題優(yōu)化的思路
    2022-12-12
  • springcloud中Feign超時(shí)提示Read timed out executing POST的問(wèn)題及解決方法

    springcloud中Feign超時(shí)提示Read timed out executing

    Feign接口調(diào)用分兩層,Ribbon的調(diào)用和Hystrix調(diào)用,理論上設(shè)置Ribbon的時(shí)間即可,但是Ribbon的超時(shí)時(shí)間和Hystrix的超時(shí)時(shí)間需要結(jié)合起來(lái),這篇文章給大家介紹springcloud之Feign超時(shí)提示Read timed out executing POST問(wèn)題及解決方法,感興趣的朋友一起看看吧
    2024-01-01

最新評(píng)論

长丰县| 建宁县| 拜城县| 铜陵市| 济源市| 雅安市| 高雄市| 泌阳县| 郴州市| 锦屏县| 商河县| 荆门市| 贺兰县| 沧州市| 保德县| 丹阳市| 鹤岗市| 宁夏| 岳阳县| 贞丰县| 罗甸县| 秦皇岛市| 淅川县| 闵行区| 阿荣旗| 手机| 泊头市| 卓尼县| 景谷| 尖扎县| 红安县| 广昌县| 沂源县| 河津市| 治多县| 龙口市| 临泽县| 盐亭县| 玉林市| 遂溪县| 酉阳|