测试之前从redis到数据库同步功能,一直是但进程在测。今天不小心多开了一个同步进程,发现bug。

Redis中的数据结构是一个列表,并且是不断动态增长,同步程序是一个定时任务程序,每隔n秒从Redis中读出数据(姑且称为为消费者程序,只是不清数据),然后插入DB中,持久化后不能清除Redis中的数据,所以要记录上一次读列表的结束位置。

起初我使用了一个全局变量RedisListIndex,用来记录该任务本次读的结束位置,也是下一次读的起始位置。该全局变量由Redis的生产者维护。

代码片段


int idx = RedisCluster.RedisListIndex; //全局变量
						mCluster.setCurrentPostion(idx);//设置本次读的结束位置,作为下一次的起始位置
						Log.info("idx = " + idx);
						ArrayList<String> list;
						if (0 == mCluster.getLastPostion()) {
							list = (ArrayList<String>) mCluster.lrang(queue, 0, idx);
						} else {
							Log.info("mCluster.getLastPostion() = " + mCluster.getLastPostion() + " and idx = " + idx);
							list = (ArrayList<String>) mCluster.lrang(queue, mCluster.getLastPostion(), idx);
						}
						Log.info("list =" + list);


						// TODO 写入数据库

						mDao.insertCashTradeInfo(list);




 


以上代码能够完成任务,但是如果进程意外退出,或者多进程同时运行,RedisListIndex必然会清空或者被多个生产者竞争,就出现数据重复错乱,由于数据表设置了主键约束,遇到相同主键数据库也会就会报错(列表里包含主键的相关字段数值)。

所以我考虑到使RedisListIndex 能够保存在磁盘中。对于java的文件操作不是很熟悉。 自己封装了一个带有互斥锁功能的文件控制器。

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Date;


/**
 * 文件操作类
 * @author JP 
 */
public class FileOptration {
	
	private RandomAccessFile Raf;
	private FileChannel channel = null;  
	private FileLock lock = null; 
	
	public FileOptration(String path,String mode) throws IOException{
		Raf = new RandomAccessFile(path,mode);  
		//Raf.seek(Raf.length());//raf在文件末尾追加内容的处理  
		channel = Raf.getChannel();
		lock = channel.lock();//独占锁 
	}


	/**
	 * @Title fileWriteWithLock
	 * @description 写文件操作,该操作带有互斥锁,文件同时只能被一个进程写。如果该锁已经被占用,则进程阻塞。
	 * @param buf 写内容
	 * @author JP
	 * @date 创建时间:2015-08-03
	 */
	public void fileWriteWithLock(String buf) throws IOException{
//      互斥操作  
        ByteBuffer sendBuffer=ByteBuffer.wrap((new Date()+buf).getBytes());  
        channel.write(sendBuffer);  
	}
	
	/**
	 * @Title fileClose
	 * @description 关闭文件,施放锁。
	 * @param buf 写内容
	 * @author JP
	 * @date 创建时间:2015-08-03
	 */
	public void fileClose(){	  
        if(lock != null) {  
            try {  
                lock.release();  
                lock = null;  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        if(channel != null) {  
            try {  
                channel.close();  
                channel = null;  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
	}
}



消费者和生产者通过

FileOptration 来把RedisListIndex在一个加锁文件中维护起来,就可以避免上述问题。

虽然是解决了,但心里总觉的乖乖的,一个文件至维护了一个变量而已,RedisListIndex竟然成了环境变量,该方案未免有些笨重。希望大神们能提出更加高大上的解决方案供我参考。