最近要设计个缓存系统,所以到处看了看,简单看了ehcache,不得不感叹源码太多了,能写成这样也I服了Y,还是像google的guava看齐吧,当然ehcache号称支持分布式
1简单使用
记录下简单使用,主要是看源码的时候可以找到入口
http://passover.blog.51cto.com/2431658/486709
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
maxElementsInMemory="5" //缓存中允许创建的最大对象数
maxElementsOnDisk = "5"
eternal="false"//缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds="1440" //缓存数据的钝化时间,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值
diskPersistent="false"
timeToLiveSeconds="2880" //缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值
overflowToDisk="false"//内存不足时,是否启用磁盘缓存
memoryStoreEvictionPolicy="FIFO"//缓存满了之后的淘汰算法
statistics="true"
/>
public class EhcacheTest {
private static final Logger logger = Logger.getLogger(EhcacheTest.class);
private static Cache sampleCache = null;
public static void main(String[]args) {
init();
test();
}
private static void test() {
logger.info(sampleCache.getMemoryStoreEvictionPolicy());
for(inti=0; i<10; i++){
//写入缓存
sampleCache.put(new Element(i, "v" + i));
//打印当前缓存的所有值
logger.info(sampleCache.getKeys());
//读取缓存
Element e = sampleCache.get(i);
logger.info(e.getValue());
}
//打印命中统计
logger.info(sampleCache.getStatistics());
}
private static voidinit() {
CacheManager manager = CacheManager.create();
// manager.addCache("sample"); //已经在配置文件定义过了
sampleCache = manager.getCache("sample");
}
}
另一种使用方式:
String name = "Namespace";
intcapacity = 500;
intrefreshPeriod = 5000;
// Initialize EhCache
Cache cache = new Cache(name, capacity, false, false, refreshPeriod, 0);
cache.initialise();
System.out.println(
"Initialize EhCache: " +
" name: " + name +
" capacity: " + capacity +
" expire: " + refreshPeriod
);
// Set data into EhCache
String key1 = "Key";
String value1 = "Value";
Element element1 = new Element(key1, value1);
cache.put(element1);
System.out.println("Set (" + key1 + ", " + value1 + ") into EhCache.");
// Get data from EhCache
Element element2 = cache.get(key1);
String key2 = (String) element2.getObjectKey();
String value2 = (String) element2.getObjectValue();
System.out.println("Get (" + key2 + ", " + value2 + ") from EhCache.");
// Remove data from EhCache
if (cache.remove(key2)) {
System.out.println("Remove data with key = " + key2 + " successfully.");
}
// Get EhCache size
System.out.println("EhCache size is " + cache.getSize());
最重要的入口就是CacheManager和Cache,Manager起到类似工厂的作用,主要还是看Cache
2CacheManager
CacheManager,默认会去找ehcache.xml以及ehcache-failsafe.xml等文件,最后创建的是一个Cache对象
private void addConfiguredCaches(ConfigurationHelper configurationHelper) {
// 根据配置创建缓存Cache对象
Set unitialisedCaches = configurationHelper.createCaches();
for (Iterator iterator = unitialisedCaches.iterator();
iterator.hasNext();) {
EhcacheunitialisedCache = (Ehcache) iterator.next();
//调用Cache的init方法
addCacheNoCheck(unitialisedCache, true);
......
}
}
}
============================================================
//在Cache的init方法中,会根据不同的淘汰策略,选择不同的Store
if (useClassicLru &&
configuration.getMemoryStoreEvictionPolicy().equals(
MemoryStoreEvictionPolicy.LRU)) {
//老的LRU策略,选择LruMemoryStore
Store disk = createDiskStore();
store = new LegacyStoreWrapper(new LruMemoryStore(this, disk), disk,registeredEventListeners, configuration);
} else {
//可以存到磁盘,DiskBackedMemoryStore(Memory和DiskStore的整合)
//新的淘汰机制,貌似会有些问题
if (configuration.isOverflowToDisk()) {
store = DiskBackedMemoryStore.create(this, onHeapPool, onDiskPool);
} else {
//只在内存上,MemoryOnlyStore
store = MemoryOnlyStore.create(this, onHeapPool);
}
}
store可以有很多实现,包括DiskBackedMemory(使用DiskStore)或者MemoryOnlyStore(使用MemoryStore)或者
LocalTransactionStore/JtaLocalTransactionStore之类的,分布式的靠Listener去实现,cache的put、update、remove、evict等操作都会发出事件,然后由listener去实现,比如RMISynchronousCacheReplicator
这些store的实现都和ConcurrentHashMap类似,也是分段,默认分64个段即64个锁
3淘汰机制
LFU最不经常使用,计算频率,也就是get的次数
FIFO先进先出,计算下写入时间
LRU最近最少使用,有些使用链表,有些只是记录一个最后访问时间
是否要淘汰都是根据最大容量,超过最大容量后,就会根据指定的策略淘汰数据,先看看新的淘汰算法吧
3.1新的Store
主要看看MemoryStore,在Store内部,有一个SelectableConcurrentHashMap,和ConcurrentHashMap很像,也是采用分段机制,但是他的淘汰机制好像非常不好,没有使用链表的形式,而是在所有元素中遍历,
//先找到要淘汰的个数
intevict = Math.min(map.quickSize() - maximumSize, MAX_EVICTION_RATIO);
for (inti = 0; i
//每次随机选出几个,再确定最应该淘汰的一个,效率不高removeElementChosenByEvictionPolicy(elementJustAdded);
}
public Element[] getRandomValues(finalintsize, Object keyHint) {
ArrayList sampled = new ArrayList(size * 2);
// pick a random starting point in the map
intrandomHash = rndm.nextInt();
finalintsegmentStart;
if (keyHint == null) {
segmentStart = (randomHash >>> segmentShift) & segmentMask;
} else {
segmentStart = (hash(keyHint.hashCode()) >>> segmentShift) & segmentMask;
}
intsegmentIndex = segmentStart;
do {
final HashEntry[] table = segments[segmentIndex].table;
finalinttableStart = randomHash & (table.length - 1);
inttableIndex = tableStart;
do {
for (HashEntry e = table[tableIndex]; e != null; e = e.next) {
Element value = e.value;
if (value != null && (!(e.pinned && elementPinningEnabled) || value.isExpired())) {
sampled.add(value);
}
}
if (sampled.size() >= size) {
return sampled.toArray(new Element[sampled.size()]);
}
//move to next table slot
tableIndex = (tableIndex + 1) & (table.length - 1);
} while (tableIndex != tableStart);
//move to next segment
segmentIndex = (segmentIndex + 1) & segmentMask;
} while (segmentIndex != segmentStart);
return sampled.toArray(new Element[sampled.size()]);
}
整了半天,貌似是为了随机定位到某个段,然后随机从某个元素开始遍历,直到找到指定个数的需要淘汰的数量,没仔细去想并发问题了,觉得效率比较低,不管了,还是
着重看下他的淘汰策略吧:
public Element selectedBasedOnPolicy(Element[] sampledElements, Element justAdded) {
//edge condition when Memory Store configured to size 0
if (sampledElements.length == 1) {
return sampledElements[0];
}
Element lowestElement = null;
for (Element element : sampledElements) {
if (element == null) {
continue;
}
if (lowestElement == null) {
if (!element.equals(justAdded)) {
lowestElement = element;
}
} else if (
compare(lowestElement,
element) && !element.equals(justAdded)) {
lowestElement = element;
}
}
return lowestElement;
}
LRU LFU FIFO的核心就在对compare的实现,
LRU:比较最后访问时间
public boolean compare(Element element1, Element element2) {
return element2.getLastAccessTime()
}
LFU:比较get次数(在get的时候会累加)
public boolean compare(Element element1, Element element2) {
return element2.getHitCount()
}
FIFO:创建/修改时间
public boolean compare(Element element1, Element element2) {
return element2.getLatestOfCreationAndUpdateTime() <
element1.getLatestOfCreationAndUpdateTime();
}
3.2老的LruMemoryStore
新的那种随机定位的方式让人感觉很怪,老的LRU实际使用的是SpoolingLinkedHashMap,
他在LinkedHashMap上增加了,他的put/get/remove是synchronized的,,所以没有并发安全性问题,但是感觉效率也不太高。。就是LinkedHashMap的同步版本了
4总结
没有发现啥亮点,代码太多了,看的几部分也没啥参考价值。。。。