自己實現一個內存緩存
最近了解了下緩存技術,主要分為內存緩存 和磁盤緩存,然後分布式裏面有一些消息的推送,節點的一些處理。現在很多的用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: 元素的單元
Java代碼
- package com.cache;
- import java.util.Collection;
- public interface Store {
- // 獲得緩存名字
- public String getName();
- // 存放元素
- public Element put(Element e);
- public Collection<Element> putAll(Collection<Element> elements);
- // 獲取元素
- public Element get(Object key);
- // 清除元素
- public void clear();
- // 移除元素
- public void remove(Object key);
- public void removeAll(Object[] keys);
- // 獲得的元素長度
- public Integer size();
- }
Java代碼
- package com.cache;
- import java.util.Map;
- /**
- * 抽象類實現
- */
- public abstract class AbstractStore implements Store{
- protected Map<Object, Element> map;
- public AbstractStore(){}
- public AbstractStore(Map<Object, Element> map){
- this.map = map;
- }
- @Override
- public Element get(Object key) {
- Element e = map.get(key);
- return e;
- }
- public Map<Object, Element> 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<keys.length;i++){
- remove(keys[i]);
- }
- }
- }
Java代碼
- 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<Object, Element> map = new HashMap<Object, Element>();
- 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<Element> putAll(Collection<Element> 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);
- }
- }
- }
Java代碼
- 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) {
- }
- }
- }
Java代碼
- 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<String, Cache> MAP_NAMES_CACHE = new HashMap<String, Cache>();
- // 存放 取出 緩存對象
- 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]);
- }
- }
Java代碼
- 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;
- }
- }
Java代碼
- 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();
- }
- }
Java代碼
- package com.cache;
- import java.util.Iterator;
- import java.util.Map;
- /**
- * 檢查的的一些方式
- * @author Administrator
- *
- */
- public class CheckManager {
- protected CacheConfiguration configure;
- protected Map<Object, Element> map;
- public CheckManager(CacheConfiguration configure,Map<Object, Element> 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<hits.length;i++){
- if(hits[i] > hit){
- hits[i] = hit;
- pos = i;;
- }
- }
- return pos;
- }
- }
Java代碼
- package com.cache;
- /**
- * 幾種刪除策略
- */
- public enum EvictionType {
- LRU,LFU,FIFO
- }
Java代碼
- 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<Object> keys = new ArrayList<Object>();
- Map<Object, Element> map = cache.getAll();
- if(map != null && map.size() > 0){
- for(Entry<Object, Element> e0: map.entrySet()){
- Element e = e0.getValue();
- if(e != null && e.isExpired()){
- keys.add(e0.getKey());
- }
- }
- }
- cache.removeAll(keys.toArray());
- }
- }
Java代碼
- 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 已經在裏面的,可以參考。
自己實現一個內存緩存