post @ Ryan-Miao.github.io

1.介绍

Ehcache is an open source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. It’s the most widely-used Java-based cache because it’s robust, proven, full-featured, and integrates with other popular libraries and frameworks. Ehcache scales from in-process caching, all the way to mixed in-process/out-of-process deployments with terabyte-sized caches.

Ehcache是一个开源的,基于标准的缓存,可提升性能,卸载数据库,简化可扩展性。它是最广泛使用的基于Java的缓存,因为它是强大的,经过验证的,全功能的,并与其他受欢迎的库和框架集成。Ehcache可以从进程中缓存扩展到千万亿字节大小的高速缓存的混合进程内/进程外部署。

1.1 关于版本

EhCache分2.x和3.x两种版本,3.x的升级不兼容2.x。
2.x 的包名是net.sf.ehcache,3.x的包名是org.ehcache
spring support包里ehcache就是集成2.x的,而3.x满足JCache (aka JSR-107) 标准,与spring集成采取JCache路线。

本次学习ehcache3.

1.2 Getting Started

添加ehcache以及jsr107的依赖

1
2
compile group: 'org.ehcache', name: 'ehcache', version: '3.3.1'
compile group: 'javax.cache', name: 'cache-api', version: '1.0.0'

一个简单cache:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.junit.Before;
import org.junit.Test;

import static org.ehcache.config.builders.CacheConfigurationBuilder.newCacheConfigurationBuilder;
import static org.ehcache.config.builders.CacheManagerBuilder.newCacheManagerBuilder;
import static org.ehcache.config.builders.ResourcePoolsBuilder.heap;
import static org.junit.Assert.assertEquals;

@Test
public void testInit() {
String preConfiguredCacheName = "preConfigured";

CacheConfigurationBuilder<Long, String> configurationBuilder = newCacheConfigurationBuilder(Long.class, String.class, heap(2));
CacheManager cacheManager = newCacheManagerBuilder()
.withCache(preConfiguredCacheName, configurationBuilder)
.build(true);

Cache<Long, String> preConfiguredCache =
cacheManager.getCache(preConfiguredCacheName, Long.class, String.class);

String one = "Ryan";
preConfiguredCache.put(1L, one);
String first = preConfiguredCache.get(1L);
assertEquals(one, first);


String two = "Leslie";
preConfiguredCache.put(2L, two);
String second = preConfiguredCache.get(2L);
assertEquals(two, second);


cacheManager.close();
}

由于当前流行java配置,典型xml配置就不探究了,虽然xml配置更简洁,但我更喜欢java配置的可读性。

创建cache要点:

  1. 需要一个CacheManager来管理cache。
  2. 需要一个CacheConfiguration来制定创建cache的条件。

本例中创建CacheManager的同时也创建了一个cache,其中制定了cache的key和value的类型以及cache的数量限制: heap(2),即只允许在java堆中创建两个单位的cache。

1.3 创建cache

当然也可以创建完CacheManger后创建cache:

1
2
3
4
5
6
7
8
9
10
11
private Cache<Long, String> createCache(String cacheName, CacheManager cacheManager) {
CacheConfiguration<Long, String> cacheConfiguration = newCacheConfigurationBuilder(Long.class, String.class, heap(10))
.withSizeOfMaxObjectGraph(1000)
.withSizeOfMaxObjectSize(1000, MemoryUnit.B)
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(20, TimeUnit.HOURS)))
.build();
Cache<Long, String> myCache = cacheManager.createCache(cacheName,
cacheConfiguration);

return myCache;
}

开始的第一个例子设置了cache的大小,而这里也用到了创建cache的几个限定条件:

  • heap(10) 堆内存中最多缓存10个单位
  • withSizeOfMaxObjectGraph 遍历一个对象图的时候的最多对象数
  • withSizeOfMaxObjectSize 单个对象的最大size
  • withExpiry 过期策略
    • timeToLiveExpiration cache的生存时间,即创建cache后可以存在多久
    • timeToIdleExpiration cache的闲置时间,即最近一次使用cache之后最多可以闲置多久
    • noExpiration 永不过期

到这里,创建cache基本完毕,还有一个需要注意的是cache逐出策略以及磁盘存储。

1.4 cache的其他存储方式

Ehcache3提供了4种存储策略:

  • heap
  • offheap
  • disk
  • clustered

这4种存储方案的速率依次降低。关于cluster暂且不研究,因为当前主要做本地cache。

首先heap就是指java堆内存,这个会被GC,所以最好数量不要太大。因为太多的cache就会造成堆内存可用内存减少,容易引起GC, stop the world. 这个可以选择存储引用(reference)或者值(value), 默认为reference,不需要序列化和反序列化,速度最快。

offheap是堆外内存,不会受GC影响,但必须以value的形式存储cache,这就会序列化和反序列化,造成性能比heap要慢。想启用offheap必须要确定是否开启了-XX:MaxDirectMemorySize=size[g|G|m|M|k|K],如果没设置则默认不开启,就无法使用offheap。

disk就是磁盘存储了,需要制定存储路径,当然速度也更慢。

典型的存储方案是三层绑定,即heap->offheap->disk。

1
2
3
4
5
6
7
8
9
10
PersistentCacheManager persistentCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(new File(getStoragePath(), "myData")))
.withCache("threeTieredCache",
CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.offheap(1, MemoryUnit.MB)
.disk(20, MemoryUnit.MB, true)
)
).build(true);

1.5 关于逐出策略

当存储空间满了之后就必须选择加进去还是换出来。看官方文档,貌似不建议逐出策略,因为所有的example都没有逐出策略。在2.x版本红,我们可以选择LRU以及FIFO、LFU。在3.x中提供了EvictionAdvisor,通过继承此类可以实现采取哪种方式逐出。

为啥不建议逐出呢,因为逐出策略会使得每次加入缓存和删除缓存都要多一次甚至多次比较,耗费性能。3.x里默认貌似是缓存满了就不添加了,当然这个具体是啥也没研究了,因为官方说即便你设置逐出策略,当命中率太低时会直接忽略并自行决定合适的逐出策略。

After a certain time, if a cache determines that the configured eviction advisor rejected too many eviction candidates, the cache can decide to completely bypass the eviction advisor and evict anything it sees fit.

1.6 关于统计命中

3.x居然没有甚至还没打算是否暴露出本地统计的api,也就是说目前没有统计的办法了。但官方说因为EhCahe事先了JSR107的标准,可以采用JSR107标准中MBean的方法来统计,然而我也没找到怎么通过MBean来统计了。 这一点直接让我对3.x从入门到放弃,在没找到统计方法之前,不继续研究了!!!


post @ Ryan-Miao.github.io