package com.github.netcomm.sponge; import java.util.ArrayList; import com.github.netcomm.sponge.util.DataByteArrayOutputStream; import com.github.netcomm.sponge.util.Utilities; public abstract class BasePersistence implements PersistenceIntf { private long maxByteArray_Sz = 50 * 1024 * 1024; private ArrayList<byte[]> inMemoryDataList = new ArrayList(); private int curInMemorySz = 0; private final Object ListMutex = new Object(); private final Object WriteAndReadMutex = new Object(); private Thread thread; private int cnt; private boolean isHaveDataInPersistence = false; private DataByteArrayOutputStream theOutBytes = null; private int oneBatchWriteCnt = 20; private long isCanReleaseResTime = -1; private long isCanReleaseResMaxTime = 60 * 1000; private int writeOffset = 0; /** * @param maxByteArray_SzParm 最大允许的内存数,单位byte * @param oneBatchWriteCntParm 一次序列化批量处理的byte[]的个数 * @param isCanReleaseResMaxTimeParm 如果连续等待这么长时间还没有任何持久化的读、写操作, * 则删除相关资源,如保存序列化的文件。 * @throws Exception */ public BasePersistence(long maxByteArray_SzParm, int oneBatchWriteCntParm, int isCanReleaseResMaxTimeParm) throws Exception { maxByteArray_Sz = maxByteArray_SzParm; oneBatchWriteCnt = oneBatchWriteCntParm; isCanReleaseResMaxTime = isCanReleaseResMaxTimeParm; theOutBytes = new DataByteArrayOutputStream(1 * 1024 * 1024); //启动一个后台线程 thread = new Thread() { public void run() { processQueue(); } }; thread.setPriority(Thread.MAX_PRIORITY); thread.setDaemon(true); thread.setName("sponge Data File Writer"); thread.start(); } //添加一批数据 @Override public boolean addOneBatchBytes(byte[] bytesParm) { boolean retBool = false; cnt++; if (isCanReleaseResTime != -1) { //超过一定时间,开始释放资源. if ((System.currentTimeMillis() - isCanReleaseResTime) > isCanReleaseResMaxTime) { try { canReleaseRes(); } catch (Exception ex) { ex.printStackTrace(); } isCanReleaseResTime = -1; } } //没有超过50M内存的限制,即可以继续添加 if (curInMemorySz + bytesParm.length <= maxByteArray_Sz) { retBool = true; synchronized (ListMutex) { //添加到内存中. 这只是个临时的过程. 因为这是个抽象类,具体的持久化实现要实现自己的持久化. inMemoryDataList.add(bytesParm); //使用锁和通知机制, 只有在有数据进来时才通知持久化的调用. //采用线程通知机制, 在后台运行一个从inMemoryDataList获取数据的方法,并调用抽象方法. //具体实现类重载抽象方法,完成具体的持久化实现. ListMutex.notifyAll(); } curInMemorySz += bytesParm.length; } else { System.out.println("已经达到缓冲器系统处理上线,丢弃此次数据,数据大小 " + bytesParm.length + "。原因是磁盘IO资源不足,请确认!!!"); } return retBool; } //后台线程不断地在跑 protected void processQueue() { byte[] tmpBytes = null; try { while (true) { // Block till we get a command. synchronized (ListMutex) { while (true) { int tmpListSz = inMemoryDataList.size(); if (tmpListSz > 0) { //一批的数据还要再分多次执行. 比如一批数据有100个任务,则每次最多允许执行20个,就要分成5次执行了 //定义inMemoryDataList是一大批的数据, 而每次执行的数据为一小批 int tmpThisTimeSaveCnt = oneBatchWriteCnt; if (tmpListSz < oneBatchWriteCnt) { //如果本身这批数据就比20还少,则能执行的数据量就只有那么多了 tmpThisTimeSaveCnt = tmpListSz; } //这里才是一小批的数据 for (int i = 0; i < tmpThisTimeSaveCnt; i++) { //执行每一个任务 tmpBytes = inMemoryDataList.remove(0); curInMemorySz -= tmpBytes.length; theOutBytes.write(tmpBytes); } //一小批执行完了,后面还有好多一小批啊.不过没关系,这一小批先写到磁盘再说! break; } //如果inMemoryDataList的size<0,说明没有数据进来,则等待 ListMutex.wait(); } //TODO break后会进入到这里. 这里为什么要notify? ListMutex.notifyAll(); } //一小批的数据写到磁盘中. 因为最外层还是while循环,所以会处理接下来的一大批中剩余的好多批. if (theOutBytes.size() > 0) { synchronized (WriteAndReadMutex) { //这个是抽象方法,文件的持久化实现要将theOutBytes中的数据写到磁盘上 //TODO 关键是writeOffset的定位. 数据的内容和长度都是确定的. doWriteOneBatchBytes(theOutBytes.getData(), writeOffset, theOutBytes.size()); //重置变量,用来重新保存后面一小批要处理的数据 theOutBytes.reset(); } } } } catch (Exception e) { e.printStackTrace(); } finally { try { destroy(); } catch (Throwable ignore) { } } } //获取一批数据. 读取数据表示消费数据,有返回值表示有需要消费的数据 //如果返回值为null,说明没有要消费的数据. 即内存中或者磁盘中都没有要读取的数据了. @Override public byte[] fetchOneBatchBytes() throws SpongeException { byte[] retBytes = null; try { //注意到读和写都是共用一个锁: WriteAndReadMutex. 因此读和写都是互斥的! synchronized (WriteAndReadMutex) { //1. 读取一批数据 retBytes = doFetchOneBatchBytes(); //没有需要消费的数据,才需要释放资源. 开始计时!... //如果retBytes==null,下面的2个if判断都不会执行. if (retBytes == null) { isCanReleaseResTime = System.currentTimeMillis(); } else { //有需要消费的数据,不能释放资源! isCanReleaseResTime = -1; } //2. 没有读到数据,但是内存中有数据,则把内存中的返回. 这种情景是: 队列中的数据超过capacity,但是没有发生写入磁盘的操作 if (retBytes == null) { if (theOutBytes.size() > 0) { int tmpByteLength = Utilities.getIntFromBytes(theOutBytes.getData(), writeOffset + 2); byte[] tmpReadBytes = new byte[tmpByteLength]; System.arraycopy(theOutBytes.getData(), writeOffset, tmpReadBytes, 0, tmpByteLength); retBytes = tmpReadBytes; writeOffset += tmpByteLength; } } //3.当添加数据的时候,会首先加到inMemoryDataList中. 然后在一定条件下后台线程才会从inMemoryDataList中移除,写到theOutBytes中 //所以在检查完theOutBytes后,还要再回过头来检查这个时间段内有没有往inMemoryDataList中添加. 有的话也返回. if (retBytes == null) { synchronized (ListMutex) { if (inMemoryDataList.size() > 0) { retBytes = inMemoryDataList.remove(0); curInMemorySz -= retBytes.length; } } } } } catch (Exception ex) { ex.printStackTrace(); throw new SpongeException(ex.getMessage()); } return retBytes; } public abstract byte[] doFetchOneBatchBytes() throws Exception; public abstract void doWriteOneBatchBytes(byte[] writeBytesParm, int offsetParm, int lengthParm) throws Exception; public abstract void doWriteOneBatchBytes(byte[] writeBytesParm) throws Exception; public abstract void destroy() throws Exception; public abstract void canReleaseRes() throws Exception; public boolean isHaveDataInPersistence() { return isHaveDataInPersistence; } public void setHaveDataInPersistence(boolean isHaveDataInPersistence) { this.isHaveDataInPersistence = isHaveDataInPersistence; } }