java中关于Ehcache缓存框架的源码剖析

来源:互联网 发布:金山毒霸软件下载 编辑:程序博客网 时间:2024/05/18 02:49
这里主要向大家讲解一下Ehcache中硬盘存储数据是怎样操作的以及数据怎样从内存到硬盘的整个过程,下面对部分源码进行剖析。
1、public class Cache implements Ehcache {
  public void initialise() {
---------------------此处省略其他代码----------------------
   if (overflowToDisk)//内存不够是否启用硬盘存储
    diskStore = new DiskStore(this, diskStorePath);//硬盘存储
   memoryStore = MemoryStore.create(this, diskStore);//内存存储
   if (diskPersistent)
    addShutdownHook();//关闭缓冲
   changeStatus(Status.STATUS_ALIVE);
  }
---------------------此处省略其他代码----------------------
}
private void addShutdownHook() {
  Thread localShutdownHook = new Thread() {
   public void run() {
    synchronized (this) {
     if (status.equals(Status.STATUS_ALIVE)) {
      shutdownHook = null;
      if (Cache.LOG.isDebugEnabled())
       Cache.LOG
         .debug("VM shutting down with the disk store for "
           + name
           + " still active. The disk store is persistent. Calling dispose...");
      dispose();//当java虚拟机重启或者关闭都将被调用,用于把缓冲区的数据写入硬盘
     }
    }
   }
  };
  Runtime.getRuntime().addShutdownHook(localShutdownHook);//注册java虚拟机ShutdownHook处理程序
  shutdownHook = localShutdownHook;
}
public synchronized void dispose() throws IllegalStateException {
  checkStatus();
  memoryStore.dispose();//释放内存空间
  memoryStore = null;
  if (overflowToDisk) {//当内存不够的时是否将数据写入硬盘
   diskStore.dispose();//将数据写入硬盘
   diskStore = null;
  }
  registeredEventListeners.dispose();
  changeStatus(Status.STATUS_SHUTDOWN);
  if (diskPersistent)
   removeShutdownHook();
}
public final void put(Element element) throws IllegalArgumentException,
   IllegalStateException, CacheException {
  put(element, false);
}
public final void put(Element element, boolean doNotNotifyCacheReplicators)
   throws IllegalArgumentException, IllegalStateException,
   CacheException {
  ------------------------------此处代码省略-----------------------------
  synchronized (this) {
   memoryStore.put(element);//将数据存入内存
  }
  if (elementExists) //数据存在时监听更新
   registeredEventListeners.notifyElementUpdated(element,
     doNotNotifyCacheReplicators);
  else//数据不存在是监听添加
   registeredEventListeners.notifyElementPut(element,
     doNotNotifyCacheReplicators);
}
2、public abstract class MemoryStore implements Store {
   // 根据不同缓存策略创建内存缓冲对象
    public static MemoryStore create(Ehcache cache, DiskStore diskStore) {
  MemoryStore memoryStore = null;
  MemoryStoreEvictionPolicy policy = cache.getMemoryStoreEvictionPolicy();
  if (policy.equals(MemoryStoreEvictionPolicy.LRU))
   memoryStore = new LruMemoryStore(cache, diskStore);
  else if (policy.equals(MemoryStoreEvictionPolicy.FIFO))
   memoryStore = new FifoMemoryStore(cache, diskStore);
  else if (policy.equals(MemoryStoreEvictionPolicy.LFU))
   memoryStore = new LfuMemoryStore(cache, diskStore);
  return memoryStore;
}
public final synchronized void dispose() {
  if (status.equals(Status.STATUS_SHUTDOWN)) {
   return;
  } else {
   status = Status.STATUS_SHUTDOWN;
   flush();//销毁时刷新缓存
   cache = null;
   return;
  }
}
public final synchronized void flush() {
  if (cache.isOverflowToDisk()) {
   if (LOG.isDebugEnabled())
    LOG.debug(cache.getName() + " is persistent. Spooling "
      + map.size() + " elements to the disk store.");
   spoolAllToDisk();//将所有数据放入到硬盘中
   clear();
  }
}
//取出内存中所有的数据并放入到硬盘存储
protected final void spoolAllToDisk() {
  Object keys[] = getKeyArray();
  for (int i = 0; i < keys.length; i++) {
   Element element = (Element) map.get(keys[i]);
   if (!element.isSerializable()) {
    if (LOG.isDebugEnabled())
     LOG
       .debug("Object with key "
         + element.getObjectKey()
         + " is not Serializable and is not being overflowed to disk.");
   } else {
    spoolToDisk(element);
    remove(keys[i]);
   }
  }
}
protected final void spoolToDisk(Element element) {
  diskStore.put(element); //此处调用硬盘存储
  if (LOG.isDebugEnabled())
   LOG.debug(cache.getName() + "Cache: spool to disk done for: "
     + element.getObjectKey());
}
}

public class DiskStore implements Store {
private final class ExpiryThread extends Thread {
  public final void run() {
   expiryThreadMain();//处理硬盘数据过期操作
  }
  public ExpiryThread() {
   super("Store " + name + " Expiry Thread");
   setDaemon(true);
   setPriority(1);// 设置线程的优先级
  }
}
private final class SpoolThread extends Thread {
  public final void run() {
   spoolThreadMain();//负责把缓冲区的数据放入磁盘文件中
  }
  public SpoolThread() {
   super("Store " + name + " Spool Thread");
   setDaemon(true);
   setPriority(2);
  }
}
//DiskElement 对应磁盘数据文件中每个独立区域
private static final class DiskElement implements Serializable {
  private static final long serialVersionUID = -717310932566592289L;
  private long position;//记录硬盘文件中每一个区的起始位置
  private int payloadSize;//数据实际大小
  private int blockSize;//文件区域中每个区域的大小
  private long expiryTime;//区域失效日期
  private DiskElement() {
  }
}
public DiskStore(Ehcache cache, String diskPath) {
  diskElements = Collections.synchronizedMap(new HashMap());//对HashMap使用锁控制,记录文件中存储的每个区域的数据元素
  freeSpace = Collections.synchronizedList(new ArrayList());//记录磁盘文件中已经释放的区域元素
  status = Status.STATUS_UNINITIALISED;
  this.cache = cache;
  name = cache.getName();
  this.diskPath = diskPath;//缓存目录
  expiryThreadInterval = cache.getDiskExpiryThreadIntervalSeconds();
  persistent = cache.isDiskPersistent();
  try {
   initialiseFiles();//初始化数据文件和index文件(记录数据文件缓冲区和空闲区的位置和大小)
   active = true;
   spoolThread = new SpoolThread();
   spoolThread.start();//启动线程开始执行数据到硬盘的写入操作
   if (!cache.isEternal()) {
    expiryThread = new ExpiryThread();
    expiryThread.start();//启动线程开始执行硬盘数据过期草组,并释放区域
   }
   status = Status.STATUS_ALIVE;
  } catch (Exception e) {
   dispose();
   LOG.error(name
     + "Cache: Could not create disk store. Initial cause was "
     + e.getMessage(), e);
  }
}
private void initialiseFiles() throws Exception {
File diskDir = new File(diskPath);//存储目录
dataFile = new File(diskDir, getDataFileName());//数据文件
  indexFile = new File(diskDir, getIndexFileName());//记录数据文件区域的文件
  randomAccessFile = new RandomAccessFile(dataFile, "rw");//可进行数据的读取和写入
}
下面看一下数据写入磁盘文件过程
private void spoolThreadMain() {
---------------------此处代码省略---------------------
   try {
    flushSpool();//把缓冲区spool中的数据写入磁盘文件操作
   } catch (Throwable e) {
    LOG.error(name
      + "Cache: Could not flush elements to disk due to "
      + e.getMessage() + ". Continuing...", e);
   }
  } while (true);
}
private synchronized void flushSpool() throws IOException {
  Object spoolCopy[] = createCopyOfSpool();//复制缓冲区的所有的数据
  for (int i = 0; i < spoolCopy.length; i++) {
   writeOrReplaceEntry(spoolCopy[i]);//如果数据已经存在就替换否则直接写入
   spoolCopy[i] = null;
  }
}
private void writeOrReplaceEntry(Object object) throws IOException {
  Element element = (Element) object;
  if (element == null) {
   return;
  } else {
   Serializable key = (Serializable) element.getObjectKey();//取出可序列化的数据
   removeOldEntryIfAny(key);//如果数据存在就先删除
   writeElement(element, key); //写入数据
   return;
  }
}
private void removeOldEntryIfAny(Serializable key) {
  DiskElement oldBlock;
  synchronized (diskElements) {//将缓存区的数据删除
   oldBlock = (DiskElement) diskElements.remove(key);
  }
  if (oldBlock != null)
   freeBlock(oldBlock);//释放缓存区的数据暂用的空间
}
private void writeElement(Element element, Serializable key)
   throws IOException {
  try {
   long expirationTime = element.getExpirationTime();//获取数据过期日期
   MemoryEfficientByteArrayOutputStream buffer = null;
   try {
    buffer = serializeEntry(element);//将对象序列化成字节数组
   } catch (OutOfMemoryError e) {
    LOG.error("OutOfMemoryError on serialize: " + key);
   }
   int bufferLength = buffer.size();
   DiskElement diskElement = checkForFreeBlock(bufferLength);//检查是否有释放的空闲区,如果没有就将数据从文件的末尾位置开始写入
   randomAccessFile.seek(diskElement.position);//写入区域的开始位置
   randomAccessFile.write(buffer.toByteArray(), 0, bufferLength);//开始写入数据
   buffer = null;
   diskElement.payloadSize = bufferLength;//记录数据实际大小
   diskElement.expiryTime = expirationTime;//记录数据过期时间
   totalSize += bufferLength;//记录所有数据的大小
   synchronized (diskElements) {
    diskElements.put(key, diskElement);//记录文件中写入数据的区域
   }
  } catch (Exception e) {
   LOG.error(name + "Cache: Failed to write element to disk '" + key
     + "'. Initial cause was " + e.getMessage(), e);
  }
}
下面介绍数据过期操作:
private void expiryThreadMain() {
  long expiryThreadIntervalMillis = expiryThreadInterval * 1000L;
  while (active)
   try {
    Thread.sleep(expiryThreadIntervalMillis);
    expireElements();//过期线程将要调用的处理方法
   } catch (InterruptedException e) {
}
}
public void expireElements() {
  long now = System.currentTimeMillis();//获取系统当期日期
  synchronized (spool) {
   Iterator iterator = spool.values().iterator();
   do {
    if (!iterator.hasNext())
     break;
    Element element = (Element) iterator.next();
    if (element.isExpired()) {//如果数据已经过期了
     if (LOG.isDebugEnabled())
      LOG.debug(name
        + "Cache: Removing expired spool element "
        + element.getObjectKey());
     iterator.remove();//这里删除已经过期的数据
     notifyExpiryListeners(element);// 监听过期数据
    }
   } while (true);
  }
  synchronized (diskElements) {
   Iterator iterator = diskElements.entrySet().iterator();
   do {
    if (!iterator.hasNext())
     break;
    java.util.Map.Entry entry = (java.util.Map.Entry) iterator
      .next();
    DiskElement diskElement = (DiskElement) entry.getValue();
    if (now >= diskElement.expiryTime) {//判断数据是否过期
     if (LOG.isDebugEnabled())
      LOG.debug(name
        + "Cache: Removing expired spool element "
        + entry.getKey() + " from Disk Store");
     iterator.remove();
     Element element = null;
     try {
//根据数据区域的起始位置和大小读取文件中的数据
      element = loadElementFromDiskElement(diskElement);
     } catch (Exception exception) {
      LOG
        .error(
          name
            + "Cache: Could not remove disk store entry for "
            + entry.getKey()
            + ". Error was "
            + exception.getMessage(),
          exception);
     }
     freeBlock(diskElement);//释放数据缓存区的空间
     notifyExpiryListeners(element);//监听失效的数据
    }
   } while (true);
  }
}
下面看一下dispose方法在jvm关闭时做的处理
public final synchronized void dispose()
    {
        if(!active)
            return;
        if(expiryThread != null)
            expiryThread.interrupt();
        flush();//刷新缓冲区并将缓冲区的所有数据写入磁盘文件中
        if(spoolThread != null)
            spoolThread.interrupt();
        spool.clear();
        diskElements.clear();
        freeSpace.clear();
      -----------------------------此处省略其他代码-----------------------------
    }
public final void flush() throws IOException {
  if (persistent) {//如果是持久化
   flushSpool();//将数据写入磁盘文件中,上面分析过了
   writeIndex();//将记录文件区域的数据写入index文件中
  }
}
private synchronized void writeIndex()
        throws IOException
    {//下面的代码使用使用java中的序列化操作
        ObjectOutputStream objectOutputStream = null;
        FileOutputStream fout = new FileOutputStream(indexFile);
        objectOutputStream = new ObjectOutputStream(fout);
        objectOutputStream.writeObject(diskElements);//写入缓存数据区域的记录数据
        objectOutputStream.writeObject(freeSpace);//写入空闲数据区域的记录数据
        if(objectOutputStream != null)
            objectOutputStream.close();
        break MISSING_BLOCK_LABEL_61;
        Exception exception;
        exception;
        if(objectOutputStream != null)
            objectOutputStream.close();
        throw exception;
    }
希望对大家有所帮助!
原创粉丝点击