1. 程式人生 > 其它 >caffeine+redis做二級快取

caffeine+redis做二級快取

caffeine+redis做二級快取

caffeine是基於Java8的本地快取(程序內快取)

  • pom.xml注入依賴

    <!-- 注入redis依賴 -->
    		<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <dependency>
    	<groupId>com.github.ben-manes.caffeine</groupId>
    	<artifactId>caffeine</artifactId>
    </dependency>
    
  • application.properties配置檔案

    #用來控制redis是否生效 1生效
    spring.redis1.enabled=1
    
  • CacheConfig 配置caffeine快取的基本資訊 名稱、超時時長、最大容量等

    import java.util.ArrayList;
    import java.util.concurrent.TimeUnit;
    
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.caffeine.CaffeineCache;
    import org.springframework.cache.support.SimpleCacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.context.annotation.Profile;
    
    import com.github.benmanes.caffeine.cache.Caffeine;
    
    @Profile("cacheenable")   //prod這個profile時快取才生效
    @Configuration
    @EnableCaching //開啟快取
    public class CacheConfig {
    	public static final int DEFAULT_MAXSIZE = 288000;
    	public static final int DEFAULT_TTL = 1000;
    
    	private SimpleCacheManager cacheManager = new SimpleCacheManager();
    	
    	// 定義cache名稱、超時時長(秒)、最大容量
    	public enum CacheEnum {
    		homePage(288000, 1000), // 有效期8個小時 , 最大容量1000
    		;
    		CacheEnum(int ttl, int maxSize) {
    			this.ttl = ttl;
    			this.maxSize = maxSize;
    		}
    
    		private int maxSize = DEFAULT_MAXSIZE; // 最大數量
    		private int ttl = DEFAULT_TTL; // 過期時間(秒)
    
    		public int getMaxSize() {
    			return maxSize;
    		}
    
    		public int getTtl() {
    			return ttl;
    		}
    	}
    
    	// 建立基於Caffeine的Cache Manager
    	@Bean
    	@Primary
    	public CacheManager caffeineCacheManager() {
    		ArrayList<CaffeineCache> caches = new ArrayList<CaffeineCache>();
    		// 最後一次寫入或訪問後經過8H過期
    		for (CacheEnum c : CacheEnum.values()) {
    			caches.add(new CaffeineCache(c.name(), Caffeine.newBuilder().recordStats()
    					.expireAfterAccess(c.getTtl(), TimeUnit.SECONDS).maximumSize(c.getMaxSize()).build()));
    		}
    		cacheManager.setCaches(caches);
    		return cacheManager;
    	}
    
    	@Bean
    	public CacheManager getCacheManager() {
    		return cacheManager;
    	}
    }
    
  • 程式碼

    /**
     * value 對應的是CacheConfig中的cache名稱
     * sync 是否非同步
     * 
     */
    @Cacheable(value = "homePage", key = "'live'.concat(':').concat(#source).concat(':').concat(#channelId)", sync = true)
    	public String makeLiveUrl(Long channelId, Integer source) {
    		
    		// 作成url
    		String prefixUrl = "";
    		if (redis1enabled == 1) {
    			logger.info("從redis中獲取資料");
    			Object redisResultUrl = redisUtil.get("live:" + source + ":" + String.valueOf(channelId));
    			if (redisResultUrl == null) {
    				logger.info("從資料庫中獲取資料");
                    // getUrlForDb 取資料庫中的資料
    				prefixUrl = getUrlForDb(channelId, source, UrlTypeEnum.LIVE_TYPE.getValue());
    
    				if (prefixUrl == null) {
    					redisUtil.set("live:" + source + ":" + String.valueOf(channelId), "-1", 0);
    				} else {
    					redisUtil.set("live:" + source + ":" + String.valueOf(channelId), prefixUrl, 0);
    				}
    			} else {
    				if (redisResultUrl.equals("-1")) {
    					prefixUrl = null;
    				} else {
    					prefixUrl = (String) redisResultUrl;
    				}
    			}
    		} else {
    			prefixUrl = getUrlForDb(channelId, source, UrlTypeEnum.LIVE_TYPE.getValue());
    		}
    
    		return prefixUrl;
    	}
    

因為 Spring Cache是基於切面的(基於AOP的動態代理實現的:即都在方法呼叫前後去獲取方法的名稱、引數、返回值,然後根據方法名稱、引數生產快取的key,進行快取),所以內部方法呼叫不會呼叫切面,導致快取不生效,可以寫一個工具類,使用內部呼叫的時候,自己例項化一個物件,讓類走AOP