ehcache - downgoon/hello-world GitHub Wiki

Getting Started

EhCache两个重要概念:CacheCacheManager,其中CacheManager管理着从Name到Cache的映射关系。

EhCache既支持XML配置形式,又支持直接写Java代码(现在倡导零配置)。

如果你觉得用EhCache要学习EhCache的API,改天用别的Cache,又要学习别的API,很麻烦,而且代码中无法替换实现方。怎么办?别担心,JAVA官方定义了 [JSR-107, javax.cache API](http://www.ehcache.org/documentation/3.2/107.html) 标准。

关于JSR-107标准:

The JCache (aka JSR-107) specification defines the standard caching API for Java. The specification was developed under the Java Community Process v2.9 by an expert group including members from the Ehcache developer community. JCache provides a very simple API set that is easy to use and vendor neutral.

标准对开发者的价值:

it is easier for an application developer to switch between products by different vendors and choose the one that suits them best without changing a single line of their application code interacting with caches.

JCache

JCache 概念

Java Cache 标准中有3个概念:

  • CachingProvider: 创建和访问CacheManager
  • CacheManager: 创建和访问Cache
  • Cache: 读写具体Key。

CachingProvider 如何寻找最终的Cache供应商呢?它的工作机制完全类似于org.slf4j,直接从application classpath寻找JCache的实现类,必须是“有且仅有”一个实现类的时候,才能正确加载,否则提示有多个供应商。当然也可以在代码中显式指定供应商的类全名,比如:

默认的供应商:必须有且仅有一个

CachingProvider provider = Caching.getCachingProvider();

指定供应商:以EhCache为例

CachingProvider provider = Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider");

样例代码

样例代码演示了:CachingProvider -> CacheManager -> Cache 的过程。

import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.Caching;
import javax.cache.configuration.MutableConfiguration;
import javax.cache.expiry.CreatedExpiryPolicy;
import javax.cache.expiry.Duration;
import javax.cache.spi.CachingProvider;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JCacheExample {

	private static final Logger LOG = LoggerFactory.getLogger(JCacheExample.class);

	public static void main(String[] args) {
		CachingProvider provider = Caching.getCachingProvider(); // #1

		CacheManager cacheManager = provider.getCacheManager(); // #2

		MutableConfiguration<Long, String> configuration = new MutableConfiguration<Long, String>() // #3
				.setTypes(Long.class, String.class) // #4
				.setStoreByValue(false) // #5
				.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE)); // #6

		Cache<Long, String> cache = cacheManager.createCache("jCache", configuration); // #7
		cache.put(1L, "one"); // #8
		String value = cache.get(1L); // #9

		LOG.info("value cached: {}", value);
	}

}

这段代码 #7 需要说明下:

Cache<Long, String> cache = cacheManager.createCache("jCache", configuration); // #7


JCache标准把 ``cacheManager.createCache``与``cacheManager.getCache``分开了,而不是类似``LoggerFactory.getLog(Some.Class)`` 如果没有,则自动创建。

另外,创建一个Cache的时候,就要针对这个Cache进行配置。一个Cache有哪些常见的配置项呢?

- Key/Value的类型: ``.setTypes(Long.class, String.class)`` Key/Value的类型,如果涉及到持久化或者网络,还需要涉及序列化机制。

- 深浅拷贝:``.setStoreByValue(false)`` 在JVM里面,put一个Value的时候,是类似map.put("Key", valueObject)的浅拷贝;还是要克隆一个对象的深拷贝。官方文档的原文是“``configured to store the cache entries by reference (not by value)``”

- 过期/驱逐策略:`` .setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE)) `` 一分钟过期。当内存不够的时候,怎么删除缓存数据,即使它还没有过期。

- 持久化: 内存不足的时候,是否写入磁盘。
- 数据一致性策略: 如何跟源数据保持一致,是否支持事务等。
- 分布式支持:缓存数据更新是否支持在分布式成员间同步。


**温馨提醒**

JCache 标准定义的Cache配置项很简单,很易用。但是与此同时,简单的往往不够丰富。EhCache作为提供商,比标准定义往往配置更丰富,如果我们一方面需要用标准JCache API,以解耦业务代码对具体Cache供应商的依赖;另一方面又想利用特定供应商的特有特性,EhCache也是有办法解决这个问题的。具体参考:[starting-from-jsr-107-created-caches](http://www.ehcache.org/documentation/3.2/107.html#starting-from-jsr-107-created-caches)

**演示代码**

上述演示代码如何获得:

$ git clone -b ehcache https://github.com/downgoon/hello-world.git $ git checkout ehcache $ git checkout ehcache-c1-jcacheapi


---

## EhCache API

ehcache api 跟 jcache 标准还是很像。

Cache<Long, String> myCache = cacheManager.createCache("myCache", cacheConfiguration);


也围绕 ``CacheManager``,``CacheConfiguration`` 和 ``Cache`` 的概念。

样例代码:

import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.CacheConfiguration; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory;

public class EhCacheExample {

private static final Logger LOG = LoggerFactory.getLogger(EhCacheExample.class);

public static void main(String[] args) {
	
	// cacheConfiguration
	CacheConfigurationBuilder<Long, String> cacheConfigurationBuilder = CacheConfigurationBuilder.newCacheConfigurationBuilder(
			Long.class, String.class, 
			ResourcePoolsBuilder.heap(100)
		);
	
	CacheConfiguration<Long, String> cacheConfiguration = cacheConfigurationBuilder.build();
	
	// cacheManager
	CacheManagerBuilder<CacheManager> cacheManagerBuilder = CacheManagerBuilder.newCacheManagerBuilder();
	CacheManager cacheManager = cacheManagerBuilder.build(true);
	

	// cache 
	Cache<Long, String> myCache = cacheManager.createCache("myCache", cacheConfiguration);
	myCache.put(1L, "da one!");
	String value = myCache.get(1L);
	LOG.info("value is: {}", value);
	
	cacheManager.close();
}

}


获得样例代码:

$ git clone -b ehcache https://github.com/downgoon/hello-world.git $ git checkout ehcache $ git checkout ehcache-c2-ehcacheconfjava


参考资料:
- [getting-started.html#configuring-with-java](http://www.ehcache.org/documentation/3.2/getting-started.html#configuring-with-java)

# Cache 存储分层体系

## off-heap 缓存

> Remember that data stored off-heap will have to be serialized and deserialized - and is thus **slower than heap**. You should thus favor **``off-heap for large amounts of data``** where on-heap would have too severe an impact on garbage collection.

>Do not forget to define in the java options the -``XX:MaxDirectMemorySize`` option, according to the off-heap size you intend to use.

off-heap 缓存

- **存储空间**:空间不是在 JVM 的 Heap上,而是 ``DirectMemory``  (需要用 ``-XX:MaxDirectMemorySize`` JVM 参数来分配 )。
- **对比heap空间**: 由于off-heap的缓存,需要进行对象的序列化和反序列化,因此速度肯定是要比heap缓存差的。那为什么又需要off-heap呢? 这是因为当缓存的内容多了的时候,系统GC会非常频繁,off-heap的不会导致GC。所以off-heap适合的场景是“**``large amounts of data``**”。

## 三层体系

三层存储体系:
- heap
- off-heap (direct memory)
- disk

import org.ehcache.Cache; import org.ehcache.PersistentCacheManager; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; import org.ehcache.config.builders.ResourcePoolsBuilder; import org.ehcache.config.units.EntryUnit; import org.ehcache.config.units.MemoryUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory;

public class StorageTiersExample {

private static final Logger LOG = LoggerFactory.getLogger(StorageTiersExample.class);

public static void main(String[] args) {

	PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
			.with(CacheManagerBuilder.persistence("/tmp/ehcache/hello"))
			.withCache("threeTieredCache",
					CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
							ResourcePoolsBuilder.newResourcePoolsBuilder()
     .heap(10, EntryUnit.ENTRIES)
     .offheap(1, MemoryUnit.MB)
     .disk(20, MemoryUnit.MB)))
     .build(true);

	Cache<Long, String> threeTieredCache = persistentCacheManager.getCache("threeTieredCache", 
			Long.class,
			String.class);

    threeTieredCache.put(123L, "ABC");
    String value = threeTieredCache.get(123L);
	
    LOG.info("value: {}", value);
	
   persistentCacheManager.close();

}

}


定义 ``ResourcePoolsBuilder``

>```
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.disk(20, MemoryUnit.MB)

同时必须设定好 disk 存储的文件地址:

CacheManagerBuilder.newCacheManagerBuilder() .with(CacheManagerBuilder.persistence("/tmp/ehcache/hello"));


演示代码:

$ git checkout ehcache $ git checkout ehcache-c3-storagetiers




# 参考资料

- [Getting Started](http://www.ehcache.org/documentation/3.2/getting-started.html)
- [官方网站](http://www.ehcache.org)