前言
之前在很多项目中都用到了缓存,常用的有memcache、redis、ehcache等,这些都是目前主流的缓存中间件,今天我给大家介绍下自己写的一个轻量级的缓存框架MkCache,这个轻量级框架能够满足一般项目的使用,目前的0.1版本还比较简单只能实现线程安全的内存缓存,支持标准hash缓存、基于LRU算法的hash缓存、弱引用的hash缓存,后续会增加分布式集群支持,数据库一致性支持等功能。下面就简单介绍下如何使用和MkCache的架构和关键类。
github地址:https://github.com/feiweiwei/MkCache
如何使用MkCache
MkCache的使用非常简单,可以直接将源码放到项目的目录中,也可以引用jar包,后续会发布到OSS中这样直接在pom里就可以引用了。
普通的java对象缓存
MkCache<String, String> cache = new MkCache<String, String>(new BasicDataStore<String, String>());
String key = "Hello";
cache.put(key, "World!");
Assert.assertEquals("World!", cache.get(key));
基于LRU算法的对象缓存
MkCache<String, User> cache = new MkCache<String, User>(new LRUDataStore<String, User>(2));
String key = "fei";
User user = new User();
user.setName("fei");
String key1 = "du";
User user1 = new User();
user1.setName("du");
String key2 = "wang";
User user2 = new User();
user2.setName("wang");
cache.put(key, user);
cache.put(key1, user1);
cache.get(key);
cache.put(key2, user2);
if (cache.get(key) != null) {
Assert.assertEquals("fei", cache.get(key).getName());
}
if (cache.get(key1) != null) {
Assert.assertEquals("du", cache.get(key1).getName());
}
if (cache.get(key2) != null) {
Assert.assertEquals("wang", cache.get(key2).getName());
}
}
使用起来是不是很简单。
MkCache架构
整个MkCache缓存框架的核心类主要有这么几个,每一层都是面向接口编程这样可以方便的进行后续扩展。
类名 | 功能 |
---|---|
ValueHolder | 缓存value类型接口,所有缓存中Value类型都是实现该接口 |
BasicValueHolder | 缓存基础Vaule类,基础范型类,常用的缓存对象都可以使用该类 |
WeakValueHolder | 弱引用value数据类型,对于在缓存中需要弱引用的类型可以使用该类 |
DataStore | 缓存数据存储接口,所有缓存数据类型都实现该接口 |
BasicDataStore | 缓存数据存储基础类,基本缓存存储类型没有特殊算法,没有特殊要求的缓存都可以使用该类 |
WeakValueDataStore | 弱引用数据类型缓存数据类型 |
LRUDataStore | 缓存数据存储LRU类,基本缓存存储类型缓存所有操作采用LRU算法 |
LRUEntry | LRU链表节点,里面定义了链表节点的前后节点的数据结构 |
MkCache | MkCache缓存核心调用类,都是缓存的接口都是通过这个接口对外暴露 |
MkCache关键类实现
MkCache类
public class MkCache<K, V> {
private final DataStore<K, V> store;
public MkCache(final DataStore<K, V> dataStore) {
store = dataStore;
}
public V get(final K key) {
try {
ValueHolder<V> value = store.get(key);
if (null == value) {
return null;
}
return value.value();
} catch (MkCacheException e) {
System.out.println(e.getStackTrace().toString());
return null;
}
}
public void put(final K key, final V value) {
try {
store.put(key, value);
} catch (MkCacheException e) {
System.out.println(e.getStackTrace().toString());
}
}
public V remove(K key) {
try {
ValueHolder<V> value = store.remove(key);
return value.value();
} catch (MkCacheException e) {
System.out.println(e.getStackTrace().toString());
return null;
}
}
public void clear() {
try {
store.clear();
} catch (MkCacheException e) {
System.out.println(e.getStackTrace().toString());
}
}
}
BasicDataStore类
public class BasicDataStore<K, V> implements DataStore<K, V> {
ConcurrentHashMap<K, ValueHolder<V>> map = new ConcurrentHashMap<K, ValueHolder<V>>();
@Override
public ValueHolder<V> get(K key) throws MkCacheException {
return map.get(key);
}
@Override
public PutStatus put(K key, V value) throws MkCacheException {
ValueHolder<V> v = new BasicValueHolder<V>(value);
map.put(key, v);
return PutStatus.PUT;
}
@Override
public ValueHolder<V> remove(K key) throws MkCacheException {
return map.remove(key);
}
@Override
public void clear() throws MkCacheException {
map.clear();
}
}
LRUDataStore类是相对复杂的一个基于LRU算法实现的数据缓存类型,类里维护了一个LRU链表,所有的get、put操作都基于LRU算法实现。
public class LRUDataStore<K, V> implements DataStore<K, V> {
public LRUDataStore(int maxSize) {
super();
this.maxSize = maxSize;
}
/**
* 构造函数定义LRU缓存数据存储的链表最大长度
*/
private final int maxSize;
/**
* 定义一个ConcurrentHashMap,用于存储LRUEntry,存储数据结构为HashMap,逻辑数据结构为一个链表
*/
ConcurrentHashMap<K, LRUEntry<K, ValueHolder<?>>> map = new ConcurrentHashMap<K, LRUEntry<K, ValueHolder<?>>>();
/**
* LRU链表的首节点first
*/
private LRUEntry<K, ValueHolder<?>> first;
/**
* LRU链表的末节点last
*/
private LRUEntry<K, ValueHolder<?>> last;
@SuppressWarnings("unchecked")
@Override
public ValueHolder<V> get(K key) throws MkCacheException {
LRUEntry<K, ValueHolder<?>> entry = (LRUEntry<K, ValueHolder<?>>) getEntry(key);
if (entry == null) {
return null;
}
//获取到相应LRU节点后,将该节点移动到链表头
moveToFirst(entry);
return (ValueHolder<V>) entry.getValue();
}
@Override
public PutStatus put(K key, V value) throws MkCacheException {
LRUEntry<K, ValueHolder<?>> entry = (LRUEntry<K, ValueHolder<?>>) getEntry(key);
PutStatus status = PutStatus.DROP;
//如果LRU缓存中没有该key,则加入链表
if (entry == null) {
//如果LRU链表长度大于最大值则删除掉最后一个节点
if (map.size() >= maxSize) {
//map删除最后一个节点
map.remove(last.getKey());
//LRU链表末节点前移一个节点
removeLast();
}
entry = new LRUEntry<K, ValueHolder<?>>(key, new BasicValueHolder<V>(value));
status = PutStatus.PUT;
} else {
entry.setValue(new BasicValueHolder<V>(value));
status = PutStatus.UPDATE;
}
//根据LRU算法将该节点移动到首节点
moveToFirst(entry);
map.put(key, entry);
return status;
}
@SuppressWarnings("unchecked")
@Override
public ValueHolder<V> remove(K key) throws MkCacheException {
LRUEntry<K, ValueHolder<?>> entry = getEntry(key);
//如果能查到该entry则进行删除操作
if (entry != null) {
if (entry.getPre() != null) {
entry.getPre().setNext(entry.getNext());
}
if (entry.getNext() != null) {
entry.getNext().setPre(entry.getPre());
}
if (entry == first) {
first = entry.getNext();
}
if (entry == last) {
last = entry.getPre();
}
}
LRUEntry<K, ValueHolder<?>> oldValue = map.remove(key);
return (ValueHolder<V>) oldValue.getValue();
}
@Override
public void clear() throws MkCacheException {
this.map.clear();
this.first = this.last = null;
}
private void moveToFirst(LRUEntry<K, ValueHolder<?>> entry) {
if (entry == first) {
return;
}
if (entry.getPre() != null) {
entry.getPre().setNext(entry.getNext());
}
if (entry.getNext() != null) {
entry.getNext().setPre(entry.getPre());
}
if (entry == last) {
last = last.getPre();
}
if (first == null || last == null) {
first = last = entry;
return;
}
entry.setNext(first);
first.setPre(entry);
first = entry;
entry.setPre(null);
}
private void removeLast() {
if (last != null) {
last = last.getPre();
if (last == null)
first = null;
else
last.next = null;
}
}
private LRUEntry<K, ValueHolder<?>> getEntry(K key) {
return map.get(key);
}
}
LRUEntry类,LRU链表节点类,其中定义了一个单向链表的节点数据结构。
public class LRUEntry<K, V extends ValueHolder<?>> implements Entry<K, ValueHolder<?>> {
final K key; // non-null
ValueHolder<?> v; // non-null
LRUEntry<K, ValueHolder<?>> pre;
LRUEntry<K, ValueHolder<?>> next;
public LRUEntry<K, ValueHolder<?>> getPre() {
return pre;
}
public void setPre(LRUEntry<K, ValueHolder<?>> pre) {
this.pre = pre;
}
public LRUEntry<K, ValueHolder<?>> getNext() {
return next;
}
public void setNext(LRUEntry<K, ValueHolder<?>> next) {
this.next = next;
}
public LRUEntry(K key, V value) {
super();
this.key = key;
this.v = value;
}
@Override
public K getKey() {
return key;
}
@Override
public ValueHolder<?> getValue() {
return this.v;
}
@Override
public ValueHolder<?> setValue(ValueHolder<?> value) {
ValueHolder<?> oldValue = this.v;
this.v = value;
return oldValue;
}
}
MkCache规划
MkCache目前0.1版本是比较轻量级的一个版本,该版本还不支持集群模式,只支持基本的分布式线性部署,而且也没有自动失效机制,后续会增加分布式集群支持,数据库一致性支持等功能,还要解决缓存穿透和缓存同时失效等缓存常见问题。