最近了解了下缓存技术,主要分为内存缓存 和磁盘缓存,然后分布式里面有一些消息的推送,节点的一些处理。现在很多的用OSCache,EhCache 等等,资料很多,暂时就不多说了,我这里仅仅为了了解缓存框架的的知识,自己临时写一个缓存,后面可以实现AOP 和spring 结合。
实现功能: 1.数据都缓存到内存中 2.实现对每个元素Element的缓存时间进行控制 3.对每个缓存Cache 进行创建,以及销毁的管理 4.能对缓存存放个数进行管理 5.通过线程定时检查元素是否过期 6.可以定义缓存清除策略
实现思路: 1.Store 接口:定义常用的存取元素get,put的接口 2.AbstractStore:对接口基本方法进行实现 3.MemoryCache:对具体缓存的存取方法进行实现,并执行一定的逻辑控制 4.Cache:增加对缓存整体进行监听,销毁等操作 5.CacheManager:缓存管理器,负责管理缓存的容器
6.CacheConfiguration :一些缓存的配置属性 7.CheckManager:一些检查缓存内容的策略方法 8.EvictionType:枚举清空策略 9.CacheListener:缓存的监听,每次创建缓存就启动,然后定时检查缓存元素是否过期 10.Element: 元素的单元
package com.cache;
import java.util.Collection;
public interface Store {
// 获得缓存名字
public String getName();
// 存放元素
public Element put(Element e);
public Collection putAll(Collection elements);
// 获取元素
public Element get(Object key);
// 清除元素
public void clear();
// 移除元素
public void remove(Object key);
public void removeAll(Object[] keys);
// 获得的元素长度
public Integer size();
}
package com.cache;
import java.util.Map;
/**
* 抽象类实现
*/
public abstract class AbstractStore implements Store{
protected Map map;
public AbstractStore(){}
public AbstractStore(Map map){
this.map = map;
}
@Override
public Element get(Object key) {
Element e = map.get(key);
return e;
}
public Map getAll(){
return map;
}
@Override
public void clear() {
map.clear();
}
@Override
public Element put(Element e) {
return map.put(e.getKey(), e);
}
@Override
public void remove(Object key) {
map.remove(key);
}
@Override
public Integer size() {
return map.size();
}
@Override
public void removeAll(Object[] keys) {
for(int i =0;i
package com.cache;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* 缓存的基本方法
*/
public class MemoryCache extends AbstractStore implements Store{
private final CacheConfiguration configure;
private final static Map map = new HashMap();
private final CheckManager checkManager;
// 暂时仅弄一个构造
public MemoryCache(CacheConfiguration configure){
super(map);
this.configure = configure;
this.checkManager = new CheckManager(configure, map);
}
@Override
public String getName() {
return configure.getCacheName();
}
@Override
public Collection putAll(Collection elements) {
if(elements == null){
throw new NullPointerException("elements can't be null");
}
check(elements.size());
for(Element e : elements){
putElementStatus(e);
super.put(e);
}
return elements;
}
@Override
public synchronized Element put(Element e) {
check(1);
putElementStatus(e);
return super.put(e);
}
// 使用一次之后刷新使用过期时间,以及使用次数
// 并检查该元素是否过期
public void changeElement(Element e){
e.addHitCount();
if(!configure.getEternal()){
e.refreshLastAccessTime();
}
}
// 如果eternal 为true,表示元素永不过期,默认忽略最小元素控制
public void putElementStatus(Element e){
if(!configure.getEternal() && ! e.getIsOpen()){
e.setTimeToIdle(configure.getTimeToIdleSeconds());
e.setTimeToLive(configure.getTimeToLiveSeconds());
}else{
e.setTimeToIdle(0);
e.setTimeToLive(0);
}
}
@Override
public Element get(Object key) {
Element e = super.get(key);
if(e != null){
if(!e.isExpired()){
changeElement(e);
}else{
synchronized (this) {
remove(e.getKey());
e = null;
}
}
}
return e;
}
// 检查元素 是否为空
public boolean checkElement(Element e){
if(e == null){
throw new NullPointerException("Element can't be null ");
}
if(e.getKey() == null){
throw new NullPointerException("Element key can't be null ");
}
return true;
}
@Override
public synchronized void removeAll(Object[] keys) {
super.removeAll(keys);
}
// 检查元素是否超过了
public void check(int checkSize){
if(checkSize <= 0){
return;
}
Object[] keys = checkManager.checkConfigure(checkSize);
if(keys !=null) {
removeAll(keys);
}
}
}
package com.cache;
/**
* 这是对缓存 级的控制判断
*/
public class Cache extends MemoryCache{
private CacheConfiguration configure;
private CacheListener listener;
public Cache(CacheConfiguration configure) {
super(configure);
this.configure = configure;
if(!configure.getEternal() && configure.getIsNeedCacheCheckListener()){
listener = new CacheListener(this);
listener.start();
}
}
public CacheConfiguration getConfigure() {
return configure;
}
// 销毁
public void destory(){
try{
super.clear();
if(listener != null){
listener.interrupt();
listener.stop();
listener = null;
}
}catch (Exception e) {
}
}
}
package com.cache;
import java.util.HashMap;
import java.util.Map;
/**
* 缓存管理类
*/
public class CacheManager {
// 默认单例
private CacheManager(){}
private static class Singleton{
private static CacheManager instance = new CacheManager();
}
public static CacheManager getInstance() {
return Singleton.instance;
}
public static Map MAP_NAMES_CACHE = new HashMap();
// 存放 取出 缓存对象
public Cache getCache(String CacheName){
Cache cache = MAP_NAMES_CACHE.get(CacheName);
return cache;
}
public void putCache(Cache cache){
if(cache != null && !MAP_NAMES_CACHE.containsKey(cache.getName())){
MAP_NAMES_CACHE.put(cache.getName(), cache);
}
}
// 移除
public void remove(String cacheName){
Cache c = MAP_NAMES_CACHE.remove(cacheName);
c.destory();
}
// 关闭所有缓存
public void shutDown(){
removeAllCaches();
MAP_NAMES_CACHE.clear();
}
// 移除所有
public void removeAllCaches(){
String [] cacheNames = getCacheNames();
for(String cacheName : cacheNames){
remove(cacheName);
}
}
// 获得名字
public String[] getCacheNames(){
return MAP_NAMES_CACHE.keySet().toArray(new String[0]);
}
}
package com.cache;
/**
*
* 这些属性来自于ehcache
* 属于缓存级别的 一些控制
*/
public class CacheConfiguration {
// 缓存的名字
private String cacheName;
// 是否需要缓存 循环检查
private Boolean isNeedCacheCheckListener = false;
public Boolean getIsNeedCacheCheckListener() {
return isNeedCacheCheckListener;
}
public void setIsNeedCacheCheckListener(Boolean isNeedCacheCheckListener) {
this.isNeedCacheCheckListener = isNeedCacheCheckListener;
}
// 内存最大缓存对象数
private Integer maxElementsInMemory;
// 缓存元素是否永久有效,一旦设置true ,失效时间 将不起作用,默认false
private Boolean eternal = false;
// 设置缓存在失效前的允许闲置时间。仅当缓存不是永久有效时使用(timeToLiveSeconds != 0)
// 可选属性,默认值是0,也就是可闲置时间无穷大。
private Integer timeToIdleSeconds = 0;
// 设置缓存长允许存活时间,最大时间介于创建时间和失效时间之间.
// 仅当缓存不是永久有效时使用,默认值是0,也就是缓存存活时间无穷大。
private Integer timeToLiveSeconds = 0;
// 对象检测线程运行的时间间隔。表示对象状态的线程多长时间运行一次
// 这里暂时用来对内存对象的检查
private Integer diskExpiryThreadIntervalSeconds = 120;
// 如果缓存满了,执行清空策略
// 可选FIFO,LFU 这里要用枚举类型
// FIFO :先进先出
// LFU:最少使用,一直以来最少被使用的,缓存缓存有一个hit属性,清除hit最小的
// LRU:最近最少使用,缓存元素有个时间戳,当缓存容量满了,而又需要腾出新地方
// 来缓存的时候,那么现有的缓存缓存中时间戳离当前时间最远的缓存将被清除缓存
private String memoryStoreEvictionPolicy = EvictionType.LRU.name();
// 暂时未用
// 当缓存数量达到最大值时,允许将缓存写入到磁盘
private Boolean overflowToDisk = false;
// 磁盘中最大的缓存对象数,若是0表示无穷大
private Integer maxElementsOnDisk = 0;
// 是否在磁盘上持久化,默认false
private Boolean diskPersistent = false;
public CacheConfiguration() {
}
public CacheConfiguration(String cacheName,Integer maxElementsInMemory,
Boolean eternal,Integer timeToIdleSeconds,Integer timeToLiveSeconds,
Integer diskExpiryThreadIntervalSeconds) {
this.cacheName = cacheName;
this.maxElementsInMemory = maxElementsInMemory;
this.eternal = eternal;
this.timeToIdleSeconds = timeToIdleSeconds;
this.timeToLiveSeconds = timeToLiveSeconds;
this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
}
public String getCacheName() {
return cacheName;
}
public void setCacheName(String cacheName) {
this.cacheName = cacheName;
}
public Integer getMaxElementsInMemory() {
return maxElementsInMemory;
}
public void setMaxElementsInMemory(Integer maxElementsInMemory) {
this.maxElementsInMemory = maxElementsInMemory;
}
public Boolean getEternal() {
return eternal;
}
public void setEternal(Boolean eternal) {
this.eternal = eternal;
}
public Integer getTimeToIdleSeconds() {
return timeToIdleSeconds;
}
public void setTimeToIdleSeconds(Integer timeToIdleSeconds) {
this.timeToIdleSeconds = timeToIdleSeconds;
}
public Integer getTimeToLiveSeconds() {
return timeToLiveSeconds;
}
public void setTimeToLiveSeconds(Integer timeToLiveSeconds) {
this.timeToLiveSeconds = timeToLiveSeconds;
}
public Boolean getOverflowToDisk() {
return overflowToDisk;
}
public void setOverflowToDisk(Boolean overflowToDisk) {
this.overflowToDisk = overflowToDisk;
}
public Integer getMaxElementsOnDisk() {
return maxElementsOnDisk;
}
public void setMaxElementsOnDisk(Integer maxElementsOnDisk) {
this.maxElementsOnDisk = maxElementsOnDisk;
}
public Boolean getDiskPersistent() {
return diskPersistent;
}
public void setDiskPersistent(Boolean diskPersistent) {
this.diskPersistent = diskPersistent;
}
public Integer getDiskExpiryThreadIntervalSeconds() {
return diskExpiryThreadIntervalSeconds;
}
public void setDiskExpiryThreadIntervalSeconds(
Integer diskExpiryThreadIntervalSeconds) {
this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
}
public String getMemoryStoreEvictionPolicy() {
return memoryStoreEvictionPolicy;
}
public void setMemoryStoreEvictionPolicy(String memoryStoreEvictionPolicy) {
this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
}
}
package com.cache;
import java.io.Serializable;
/**
* 缓存元素,所对应的属性
*/
@SuppressWarnings("serial")
public class Element implements Serializable {
static final long ONE_SECOND = 1000L;
private Object key;
private Object value;
// 使用次数
private volatile long hitCount = 0;
// 这些属性是单个元素的属性控制
// 是否使用单独元素控制
private Boolean isOpen = false;
// 从创建时间开始后,还能存活时间,0 表示一直存活,超时时间=timeToLive + CreationTime
private volatile int timeToLive = 0;
// 从最近即(min(CreationTime,LastAccessTime)) 后还剩余的时间
private volatile int timeToIdle = 0;
// 创建时间
private transient long creationTime;
// 最后一次使用的时间
private transient long lastAccessTime;
// 最后更新时间
private volatile long lastUpdateTime;
// 表示是否使用cache 级别的控制,还是元素级别的控制,这里暂时不用
private volatile boolean cacheDefaultLifespan = true;
public Element(final Object key, final Object value){
init(key,value);
}
public Element(final Object key, final Object value,Boolean isOpen){
init(key, value);
this.isOpen = isOpen;
}
private void init(final Object key, final Object value){
this.key = key;
this.value = value;
this.creationTime = System.currentTimeMillis();
this.lastAccessTime = System.currentTimeMillis();
}
public Object getKey() {
return key;
}
public void setKey(Object key) {
this.key = key;
}
public Object getValue() {
return value;
}
public long getHitCount() {
return hitCount;
}
public void setHitCount(long hitCount) {
this.hitCount = hitCount;
}
public void addHitCount(){
hitCount += 1;
}
public int getTimeToLive() {
return timeToLive;
}
public void setTimeToLive(int timeToLive) {
this.timeToLive = timeToLive;
}
public int getTimeToIdle() {
return timeToIdle;
}
public void setTimeToIdle(int timeToIdle) {
this.timeToIdle = timeToIdle;
}
public long getCreationTime() {
return creationTime;
}
public void setCreationTime(long creationTime) {
this.creationTime = creationTime;
}
public long getLastAccessTime() {
return lastAccessTime;
}
public void setLastAccessTime(long lastAccessTime) {
this.lastAccessTime = lastAccessTime;
}
public long getLastUpdateTime() {
return lastUpdateTime;
}
public void setLastUpdateTime(long lastUpdateTime) {
this.lastUpdateTime = lastUpdateTime;
}
public boolean isCacheDefaultLifespan() {
return cacheDefaultLifespan;
}
public void setCacheDefaultLifespan(boolean cacheDefaultLifespan) {
this.cacheDefaultLifespan = cacheDefaultLifespan;
}
public void setValue(Object value) {
this.value = value;
}
public Boolean getIsOpen() {
return isOpen;
}
public void setIsOpen(Boolean isOpen) {
this.isOpen = isOpen;
}
/**
* 判断元素 是否过期
* @return
*/
public boolean isExpired() {
if (isEternal()) {
return false;
}
// 获得过期时间
long expirationTime = getExpirationTime();
long now = System.currentTimeMillis();
return now > expirationTime;
}
// 是否是不会过期
public boolean isEternal() {
return (0 == timeToIdle) && (0 == timeToLive);
}
// 计算过期时间
public long getExpirationTime() {
if (isEternal()) {
return Long.MAX_VALUE;
}
// 存活时间
long expirationTime = 0;
long ttlExpiry = creationTime + getTimeToLive() * ONE_SECOND;
// 到期时间
long mostRecentTime = Math.max(creationTime, lastAccessTime);
long ttiExpiry = mostRecentTime + getTimeToIdle() * ONE_SECOND;
// 如果仅仅设置了timeToLive,那么时间以 timeToLive的计算为准
if (getTimeToLive() != 0 && (getTimeToIdle() == 0 || lastAccessTime == 0)) {
expirationTime = ttlExpiry;
} else if (getTimeToLive() == 0) {
// 如果仅仅设置了 timeToIdle,那么时间以timeToIdle 的计算为准
expirationTime = ttiExpiry;
} else {
// 如果两种都设置了,那么取小的一个为准
expirationTime = Math.min(ttlExpiry, ttiExpiry);
}
return expirationTime;
}
// 刷新最后一次使用时间
public void refreshLastAccessTime(){
lastAccessTime = System.currentTimeMillis();
}
}
package com.cache;
import java.util.Iterator;
import java.util.Map;
/**
* 检查的的一些方式
* @author Administrator
*
*/
public class CheckManager {
protected CacheConfiguration configure;
protected Map map;
public CheckManager(CacheConfiguration configure,Map map) {
this.map = map;
this.configure = configure;
}
// 添加检查元素是否已经到达最大值,或者已经过期
public Object[] checkConfigure(int elementSize){
int removeSize = map.size()+elementSize - configure.getMaxElementsInMemory();
// 判断缓存是否已满
if(removeSize > 0){
// 按规则删除元素,这里不写磁盘
if(!configure.getDiskPersistent()){
return removeElementByEvictionType(removeSize);
}
}
return null;
}
// 根据方式移除
public Object[] removeElementByEvictionType(int removeSize){
if(configure.getMemoryStoreEvictionPolicy().equals(EvictionType.LRU.name())){
return removeElementByLRU(removeSize);
}
return null;
}
// 暂时默认根据最少使用次数进行删除
private Object[] removeElementByLRU(int removeSize){
Object keys[] = new Object[removeSize];
long hits[] = new long[removeSize];
Iterator> it = map.keySet().iterator();
// 找出hit值最小的 removeSize 个元素
int index = 0;
while(it.hasNext()){
Object key = it.next();
Element e = map.get(key);
long hit = e.getHitCount();
if(index < removeSize){
hits[index] = hit;
keys[index] = key;
index ++;
}else{
long pos = getMinIndex(hits, hit);
if(pos >= 0){
keys[(int) pos] = key;
}
}
}
return keys;
}
private long getMinIndex(long hits[],long hit){
long pos = -1;
for(int i = 0;i hit){
hits[i] = hit;
pos = i;;
}
}
return pos;
}
}
package com.cache;
/**
* 几种删除策略
*/
public enum EvictionType {
LRU,LFU,FIFO
}
package com.cache;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* 循环检查元素
* @author Administrator
*
*/
public class CacheListener extends Thread{
private Cache cache;
private volatile boolean stop = false;
private volatile long ONE_SECOND = 1000;
public boolean isStop() {
return stop;
}
public void setStop(boolean stop) {
this.stop = stop;
}
public CacheListener(Cache cache) {
this.cache = cache;
}
@Override
public void run() {
long time = cache.getConfigure().getDiskExpiryThreadIntervalSeconds();
try {
while(!stop){
sleep(time*ONE_SECOND);
threadCheckElement();
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
public void destory(){
ONE_SECOND = 0;
stop = true;
}
public void threadCheckElement(){
List keys = new ArrayList();
Map map = cache.getAll();
if(map != null && map.size() > 0){
for(Entry e0: map.entrySet()){
Element e = e0.getValue();
if(e != null && e.isExpired()){
keys.add(e0.getKey());
}
}
}
cache.removeAll(keys.toArray());
}
}
package com.test;
import com.cache.Cache;
import com.cache.CacheConfiguration;
import com.cache.CacheManager;
import com.cache.Element;
public class Test {
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
CacheManager cm = CacheManager.getInstance();
Cache c1 = new Cache(getConfigure());
// 最大放5个
putTestE1(c1);
cm.putCache(c1);
// 只有2 的hit 值最小,因此超过了就被移除了 null
System.out.println(c1.get(2));
// 想存放6了,实际数据只有5个
System.out.println("总数:"+c1.size());
// 休息3秒,然后使用
Thread.sleep(1000*3);
System.out.println("刷新:"+c1.get(1));
// 然后继续休息
Thread.sleep(1000*3);
System.out.println("使用着的元素:"+c1.size());
Thread.sleep(1000*15);
System.out.println("时间太久,全部过期"+c1.size());
//cm.shutDown();
}
public static CacheConfiguration getConfigure(){
CacheConfiguration c = new CacheConfiguration();
c.setCacheName("Test1");
// 最多存放5个元素
c.setMaxElementsInMemory(5);
// 假设5秒不用就过期,这两个时间一般默认选小的一个执行,最长时间是 存活的总时间
c.setTimeToIdleSeconds(5);
// 假设最长能存活115秒
c.setTimeToLiveSeconds(115);
// 6秒 检查一次过期
c.setDiskExpiryThreadIntervalSeconds(6);
return c;
}
public static void putTestE1(Cache c1){
c1.put(new Element(1, 2));
c1.get(1);
c1.put(new Element(2, 2));
//c1.get(2);
c1.put(new Element(3, 2));
c1.get(3);
c1.put(new Element(4, 2));
c1.get(4);
c1.put(new Element(5, 2));
c1.get(5);
c1.put(new Element(6, 2));
}
}
小结:
1.这里是看了一些Ehcache 的源码,加上自己的思路写的,仅做第一次尝试之用
2.关于磁盘缓存 和 分布式 的东西这里是没有的,包括异常各种处理,这里都是没有的
3.关于缓存管理,这里每个cache 都有一个线程,是需要改进的,比较消耗资源
4.测试代码,我测试了一部分,会有遗漏的,留点有兴趣的去搞吧。
5.有问题请多指点,代码,和ehcache 已经在里面的,可以参考。