天天看點

本地緩存的幾種架構Guava Cacheehcache

Guava Cache

Google Guava Cache是一種非常優秀本地緩存解決方案,提供了基于容量,時間和引用的緩存回收方式。

怎麼用?​

引入依賴

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>18.0</version>
</dependency>      

相關加載緩存代碼

public class GiteaUserServiceImpl implements GiteaUserService {
    // 緩存的list
private LoadingCache<String, Optional<CommonUserDto>> emailUserList;

    // 在項目啟動的時候構造緩存
    @PostConstruct
    public void init() {
        // 設定兩小時的過期時間
        emailUserList = CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.HOURS).build(new CacheLoader<String, Optional<CommonUserDto>>() {
            // 緩存失效之後的重新加載邏輯
            @Override
            public Optional<CommonUserDto> load(String email) {
                CommonUserDto commonUserDto = null;
                try {
                    commonUserDto = giteaExService.qryUserDtoByEmail(email);
                }
                catch (Exception e) {
                    logger.error("LoadingCache Portal User error...", e);
                }
                return Optional.ofNullable(commonUserDto);
            }
        });
        // 加載緩存
        this.loadUserDtoList();
    }
     /**
     * 查詢并且構造使用者集合
     */
    private void loadUserDtoList() {
        // 查詢全量使用者
        List<CommonUserDto> userDtoList = giteaExService.qryUserDtoList();
        // 加載緩存
        userDtoList.stream()
                .filter(commonUserDto -> !ObjectUtils.isEmpty(commonUserDto) && StringUtils.isNotEmpty(commonUserDto.getEmail()))
                .forEach(commonUserDto -> {
                    // 構造email緩存
                    emailUserList.put(commonUserDto.getEmail(), Optional.of(commonUserDto));
                });
    }
    
    
}         

load方法是緩存失效之後的重新加載邏輯,因為guava cache不允許出現null,是以使用Optional作為容器,設定null;

ehcache

Ehcache主要基于記憶體緩存,磁盤緩存為輔的,使用起來友善。支援緩存内容存儲在磁盤中。

使用方式

<dependency>
  <groupId>net.sf.ehcache</groupId>
  <artifactId>ehcache</artifactId>
  <version>2.10.4</version>
</dependency>      

在項目資源目錄下建立一個ehcache.xml檔案,檔案内容參考如下:

<ehcache>
   
    <diskStore path = "./cache"/>

    <!-- 預設的管理政策 -->
    <defaultCache
            eternal = "false"
            maxElementsInMemory = "10000"
            overflowToDisk = "true"
            diskPersistent = "false"
            timeToIdleSeconds = "604800"
            timeToLiveSeconds = "7200"
            diskExpiryThreadIntervalSeconds = "120"
            memoryStoreEvictionPolicy = "LRU"/>

    <!-- 此緩存最多可以存活timeToLiveSeconds秒,如果期間超過timeToIdleSeconds秒未通路,緩存失效 -->
    <cache
            name = "defaultCache"
            eternal = "false"
            maxElementsInMemory = "80000"
            overflowToDisk = "true"
            diskPersistent = "false"
            timeToIdleSeconds = "604800"
            timeToLiveSeconds = "604800"
            memoryStoreEvictionPolicy = "LRU"/>

    <!-- maxElementsInMemory 記憶體中最大緩存對象數,看着自己的heap大小來搞 -->
    <!-- eternal:true表示對象永不過期,此時會忽略timeToIdleSeconds和timeToLiveSeconds屬性,預設為false -->
    <!-- maxElementsOnDisk:硬碟中最大緩存對象數,若是0表示無窮大 -->
    <!-- overflowToDisk:true表示當記憶體緩存的對象數目達到了maxElementsInMemory界限後,
    會把溢出的對象寫到硬碟緩存中。注意:如果緩存的對象要寫入到硬碟中的話,則該對象必須實作了Serializable接口才行。-->
    <!-- diskSpoolBufferSizeMB:磁盤緩存區大小,預設為30MB。每個Cache都應該有自己的一個緩存區。-->
    <!-- diskPersistent:是否緩存虛拟機重新開機期資料  -->
    <!-- diskExpiryThreadIntervalSeconds:磁盤失效線程運作時間間隔,預設為120秒 -->

    <!-- timeToIdleSeconds: 設定允許對象處于空閑狀态的最長時間,以秒為機關。當對象自從最近一次被通路後,
    如果處于空閑狀态的時間超過了timeToIdleSeconds屬性值,這個對象就會過期,
    EHCache将把它從緩存中清空。隻有當eternal屬性為false,該屬性才有效。如果該屬性值為0,
    則表示對象可以無限期地處于空閑狀态 -->

    <!-- timeToLiveSeconds:設定對象允許存在于緩存中的最長時間,以秒為機關。當對象自從被存放到緩存中後,
    如果處于緩存中的時間超過了 timeToLiveSeconds屬性值,這個對象就會過期,
    EHCache将把它從緩存中清除。隻有當eternal屬性為false,該屬性才有效。如果該屬性值為0,
    則表示對象可以無限期地存在于緩存中。timeToLiveSeconds必須大于timeToIdleSeconds屬性,才有意義 -->

    <!-- memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,
    Ehcache将會根據指定的政策去清理記憶體。可選政策有:LRU(最近最少使用,預設政策)、
    FIFO(先進先出)、LFU(最少通路次數)。-->

</ehcache>      

注入bean的方式構造cacheManager

@Configuration
public class EhCacheConfig {

    /**
     * 據shared與否的設定,Spring分别通過CacheManager.create()或new CacheManager()方式來建立一個ehcache基地.
     */
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cacheManagerFactoryBean.setShared(true);
        return cacheManagerFactoryBean;
    }

}      

寫一個緩存工具類

@Component
@EnableCaching
public class CacheUtils {

    private static Cache cache;

    private static final String CACHE_NAME = "defaultCache";

    @Autowired
    private EhCacheCacheManager ehCacheCacheManager;


    public static void put(String key, Object value) {
        put(CACHE_NAME, key, value);
    }

    public static Object get(String key) {
        Element element = cache.get(key);
        if (null != element) {
            return element.getObjectValue();
        }
        return element;
    }

    @PostConstruct
    private void init() {
        EhCacheCacheManager ehCacheCacheManager = ApplicationContextHelper.getBean(EhCacheCacheManager.class);
        CacheManager cacheManager = ehCacheCacheManager.getCacheManager();
        try {
            cache = cacheManager.getCache(CACHE_NAME);
            if (cache == null) {
                throw new RuntimeException("目前系統中沒有定義“" + CACHE_NAME + "”這個緩存。");
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 寫入緩存 通用方法
     *
     * @param cacheName
     * @param key
     * @param value
     */
    private static void put(String cacheName, String key, Object value) {
        Element element = new Element(key, value);
        cache.put(element);
    }

}      

以上是兩種本地緩存的架構的簡單使用,個人使用來看Google的緩存更加簡單實用。