一、前言

  前篇博文已经分析了FileTxnLog的源码,现在接着分析持久化中的FileSnap,其主要提供了快照相应的接口。

二、SnapShot源码分析

  SnapShot是FileTxnLog的父类,接口类型,其方法如下  

public interface SnapShot {

    /**
* deserialize a data tree from the last valid snapshot and
* return the last zxid that was deserialized
* @param dt the datatree to be deserialized into
* @param sessions the sessions to be deserialized into
* @return the last zxid that was deserialized from the snapshot
* @throws IOException
*/
// 反序列化
long deserialize(DataTree dt, Map<Long, Integer> sessions)
throws IOException; /**
* persist the datatree and the sessions into a persistence storage
* @param dt the datatree to be serialized
* @param sessions
* @throws IOException
*/
// 序列化
void serialize(DataTree dt, Map<Long, Integer> sessions,
File name)
throws IOException; /**
* find the most recent snapshot file
* @return the most recent snapshot file
* @throws IOException
*/
// 查找最新的snapshot文件
File findMostRecentSnapshot() throws IOException; /**
* free resources from this snapshot immediately
* @throws IOException
*/
// 释放资源
void close() throws IOException;
}

  说明:可以看到SnapShot只定义了四个方法,反序列化、序列化、查找最新的snapshot文件、释放资源。

三、FileSnap源码分析

  FileSnap实现了SnapShot接口,主要用作存储、序列化、反序列化、访问相应snapshot文件。

  3.1 类的属性 

public class FileSnap implements SnapShot {
// snapshot目录文件
File snapDir;
// 是否已经关闭标识
private volatile boolean close = false;
// 版本号
private static final int VERSION=2;
// database id
private static final long dbId=-1;
// Logger
private static final Logger LOG = LoggerFactory.getLogger(FileSnap.class);
// snapshot文件的魔数(类似class文件的魔数)
public final static int SNAP_MAGIC
= ByteBuffer.wrap("ZKSN".getBytes()).getInt();
}

  说明:FileSnap主要的属性包含了是否已经关闭标识。

  3.2 类的核心函数

  1. deserialize函数

  函数签名如下:

  public long deserialize(DataTree dt, Map<Long, Integer> sessions),是对SnapShot的deserialize函数的实现。其源码如下  

    public long deserialize(DataTree dt, Map<Long, Integer> sessions)
throws IOException {
// we run through 100 snapshots (not all of them)
// if we cannot get it running within 100 snapshots
// we should give up
// 查找100个合法的snapshot文件
List<File> snapList = findNValidSnapshots(100);
if (snapList.size() == 0) { // 无snapshot文件,直接返回
return -1L;
}
//
File snap = null;
// 默认为不合法
boolean foundValid = false;
for (int i = 0; i < snapList.size(); i++) { // 遍历snapList
snap = snapList.get(i);
// 输入流
InputStream snapIS = null;
CheckedInputStream crcIn = null;
try {
LOG.info("Reading snapshot " + snap);
// 读取指定的snapshot文件
snapIS = new BufferedInputStream(new FileInputStream(snap));
// 验证
crcIn = new CheckedInputStream(snapIS, new Adler32());
InputArchive ia = BinaryInputArchive.getArchive(crcIn);
// 反序列化
deserialize(dt,sessions, ia);
// 获取验证的值Checksum
long checkSum = crcIn.getChecksum().getValue();
// 从文件中读取val值
long val = ia.readLong("val");
if (val != checkSum) { // 比较验证,不相等,抛出异常
throw new IOException("CRC corruption in snapshot : " + snap);
}
// 合法
foundValid = true;
// 跳出循环
break;
} catch(IOException e) {
LOG.warn("problem reading snap file " + snap, e);
} finally { // 关闭流
if (snapIS != null)
snapIS.close();
if (crcIn != null)
crcIn.close();
}
}
if (!foundValid) { // 遍历所有文件都未验证成功
throw new IOException("Not able to find valid snapshots in " + snapDir);
}
// 从文件名中解析出zxid
dt.lastProcessedZxid = Util.getZxidFromName(snap.getName(), "snapshot");
return dt.lastProcessedZxid;
}

  说明:deserialize主要用作反序列化,并将反序列化结果保存至dt和sessions中。 其大致步骤如下

  ① 获取100个合法的snapshot文件,并且snapshot文件已经通过zxid进行降序排序,进入②

  ② 遍历100个snapshot文件,从zxid最大的开始,读取该文件,并创建相应的InputArchive,进入③

  ③ 调用deserialize(dt,sessions, ia)函数完成反序列化操作,进入④

  ④ 验证从文件中读取的Checksum是否与新生的Checksum相等,若不等,则抛出异常,否则,进入⑤

  ⑤ 跳出循环并关闭相应的输入流,并从文件名中解析出相应的zxid返回。

  ⑥ 在遍历100个snapshot文件后仍然无法找到通过验证的文件,则抛出异常。

  在deserialize函数中,会调用findNValidSnapshots以及同名的deserialize(dt,sessions, ia)函数,findNValidSnapshots函数源码如下  

    private List<File> findNValidSnapshots(int n) throws IOException {
// 按照zxid对snapshot文件进行降序排序
List<File> files = Util.sortDataDir(snapDir.listFiles(),"snapshot", false);
int count = 0;
List<File> list = new ArrayList<File>();
for (File f : files) { // 遍历snapshot文件
// we should catch the exceptions
// from the valid snapshot and continue
// until we find a valid one
try {
// 验证文件是否合法,在写snapshot文件时服务器宕机
// 此时的snapshot文件非法;非snapshot文件也非法
if (Util.isValidSnapshot(f)) {
// 合法则添加
list.add(f);
// 计数器加一
count++;
if (count == n) { // 等于n则跳出循环
break;
}
}
} catch (IOException e) {
LOG.info("invalid snapshot " + f, e);
}
}
return list;
}

  说明:该函数主要是查找N个合法的snapshot文件并进行降序排序后返回,Util的isValidSnapshot函数主要是从文件名和文件的结尾符号是否是"/"来判断snapshot文件是否合法。其源码如下 

    public static boolean isValidSnapshot(File f) throws IOException {
// 文件为空或者非snapshot文件,则返回false
if (f==null || Util.getZxidFromName(f.getName(), "snapshot") == -1)
return false; // Check for a valid snapshot
// 随机访问文件
RandomAccessFile raf = new RandomAccessFile(f, "r");
try {
// including the header and the last / bytes
// the snapshot should be atleast 10 bytes
if (raf.length() < 10) { // 文件大小小于10个字节,返回false
return false;
}
// 移动至倒数第五个字节
raf.seek(raf.length() - 5);
byte bytes[] = new byte[5];
int readlen = 0;
int l;
while(readlen < 5 &&
(l = raf.read(bytes, readlen, bytes.length - readlen)) >= 0) { // 将最后五个字节存入bytes中
readlen += l;
}
if (readlen != bytes.length) {
LOG.info("Invalid snapshot " + f
+ " too short, len = " + readlen);
return false;
}
ByteBuffer bb = ByteBuffer.wrap(bytes);
int len = bb.getInt();
byte b = bb.get();
if (len != 1 || b != '/') { // 最后字符不为"/",不合法
LOG.info("Invalid snapshot " + f + " len = " + len
+ " byte = " + (b & 0xff));
return false;
}
} finally {
raf.close();
} return true;
}

  deserialize(dt,sessions, ia)函数的源码如下  

    public void deserialize(DataTree dt, Map<Long, Integer> sessions,
InputArchive ia) throws IOException {
FileHeader header = new FileHeader();
// 反序列化至header
header.deserialize(ia, "fileheader");
if (header.getMagic() != SNAP_MAGIC) { // 验证魔数是否相等
throw new IOException("mismatching magic headers "
+ header.getMagic() +
" != " + FileSnap.SNAP_MAGIC);
}
// 反序列化至dt、sessions
SerializeUtils.deserializeSnapshot(dt,ia,sessions);
}

  说明:该函数主要作用反序列化,并将反序列化结果保存至header和sessions中。其中会验证header的魔数是否相等。

  2. serialize函数 

  函数签名如下:protected void serialize(DataTree dt,Map<Long, Integer> sessions, OutputArchive oa, FileHeader header) throws IOException

    protected void serialize(DataTree dt,Map<Long, Integer> sessions,
OutputArchive oa, FileHeader header) throws IOException {
// this is really a programmatic error and not something that can
// happen at runtime
if(header==null) // 文件头为null
throw new IllegalStateException(
"Snapshot's not open for writing: uninitialized header");
// 将header序列化
header.serialize(oa, "fileheader");
// 将dt、sessions序列化
SerializeUtils.serializeSnapshot(dt,oa,sessions);
}

  说明:该函数主要用于序列化dt、sessions和header,其中,首先会检查header是否为空,然后依次序列化header,sessions和dt。

  3. serialize函数

  函数签名如下:public synchronized void serialize(DataTree dt, Map<Long, Integer> sessions, File snapShot) throws IOException  

    public synchronized void serialize(DataTree dt, Map<Long, Integer> sessions, File snapShot)
throws IOException {
if (!close) { // 未关闭
// 输出流
OutputStream sessOS = new BufferedOutputStream(new FileOutputStream(snapShot));
CheckedOutputStream crcOut = new CheckedOutputStream(sessOS, new Adler32());
//CheckedOutputStream cout = new CheckedOutputStream()
OutputArchive oa = BinaryOutputArchive.getArchive(crcOut);
// 新生文件头
FileHeader header = new FileHeader(SNAP_MAGIC, VERSION, dbId);
// 序列化dt、sessions、header
serialize(dt,sessions,oa, header);
// 获取验证的值
long val = crcOut.getChecksum().getValue();
// 写入值
oa.writeLong(val, "val");
// 写入"/"
oa.writeString("/", "path");
// 强制刷新
sessOS.flush();
crcOut.close();
sessOS.close();
}
}

  说明:该函数用于将header、sessions、dt序列化至本地snapshot文件中,并且在最后会写入"/"字符。该方法是同步的,即是线程安全的。

四、总结

  FileSnap源码相对较简单,其主要是用于操作snapshot文件,也谢谢各位园友的观看~  

【Zookeeper】源码分析之持久化--FileSnap的更多相关文章

  1. 【Zookeeper】源码分析之持久化(二)之FileSnap

    一.前言 前篇博文已经分析了FileTxnLog的源码,现在接着分析持久化中的FileSnap,其主要提供了快照相应的接口. 二.SnapShot源码分析 SnapShot是FileTxnLog的父类 ...

  2. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  3. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  4. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  5. Zookeeper 源码分析-启动

    Zookeeper 源码分析-启动 博客分类: Zookeeper   本文主要介绍了zookeeper启动的过程 运行zkServer.sh start命令可以启动zookeeper.入口的main ...

  6. 【Zookeeper】源码分析之持久化--FileTxnLog

    一.前言 前一篇已经分析了序列化,这篇接着分析Zookeeper的持久化过程源码,持久化对于数据的存储至关重要,下面进行详细分析. 二.持久化总体框架 持久化的类主要在包org.apache.zook ...

  7. 【Zookeeper】源码分析之持久化--FileTxnSnapLog

    一.前言 前面分析了FileSnap,接着继续分析FileTxnSnapLog源码,其封装了TxnLog和SnapShot,其在持久化过程中是一个帮助类. 二.FileTxnSnapLog源码分析 2 ...

  8. 【Zookeeper】源码分析之持久化(三)之FileTxnSnapLog

    一.前言 前面分析了FileSnap,接着继续分析FileTxnSnapLog源码,其封装了TxnLog和SnapShot,其在持久化过程中是一个帮助类. 二.FileTxnSnapLog源码分析 2 ...

  9. 【Zookeeper】源码分析之持久化(一)之FileTxnLog

    一.前言 前一篇已经分析了序列化,这篇接着分析Zookeeper的持久化过程源码,持久化对于数据的存储至关重要,下面进行详细分析. 二.持久化总体框架 持久化的类主要在包org.apache.zook ...

随机推荐

  1. 实现关闭ssh继续实施方案---tmux

    问题1:为什么ssh一个关闭.不再执行的程序 罪魁祸首:SIGHUP 信号 让我们来看看为什么关掉形式/断开将使程序被执行死. 在Linux/Unix在.有几个概念: 进程组(process grou ...

  2. 在vi中使用perltidy格式化perl代码

    格式优美的perl代码不但让人赏心悦目,并且能够方便阅读. perltidy的是sourceforge的一个小项目,在我们写完乱七八糟的代码后,他能像变魔术一样把代码整理得漂美丽亮,快来体验一下吧!! ...

  3. 一个只能用在Win下的密码验证函数(显示星号,可删除)

    以前做小程序时图好玩在网上找的代码.输入的密码会以星号显示出来,并且输入错了可以删除.因为用了专有库函数,所以只能在Windows平台使用,少用为好,不过可能还有点用.嗯…就这样了 #include ...

  4. Mac OSX操作系统安装和配置Zend Server 6教程(4)

    在前三节中,完成了安装和配置,最后是登录后台,并设置权限.Zend server后台登录默认端口是10081.浏览器中的的地址应输入http://localhost:10081/ZendServer. ...

  5. 使用SQL Server 2005数据库管理工具 - 初学者系列 - 学习者系列文章

    本文讲述使用SQL Server 2005 Express数据库管理工具的使用. 1.打开数据库管理工具 2.选择下面的SQL Server 身份验证,因为在安装数据库的时候设置了sa的密码. 3.选 ...

  6. Extjs grid分页多选记忆功能

    很多同事在用extjs grid做分页的时候,往往会想用grid的多选功能来实现导出Excel之类的功能(也就是所谓的多选记忆功能),但在选选择下一页的时候 上一页选中的已经清除 这是因为做分页的时候 ...

  7. css3学习文档

    什么是CSS3? CSS3是CSS2的升级版本,3只是版本号,它在CSS2.1的基础上增加了很多强大的新功能. 目前主流浏览器chrome.safari.firefox.opera.甚至360都已经支 ...

  8. 【转】【Android工具】被忽略的UI检视利器:Hierarchy Viewer

    原文:http://blog.csdn.net/ddna/article/details/5527072 Hierarchy Viewer是随AndroidSDK发布的工具,位置在tools文件夹下, ...

  9. 介绍一款基于jquery好用的编辑框htmlbox.full.js

    1. 可选择背景颜色,自主选择工具,感觉挺好用的,不过需要注意,添加引用后找不到工具图标的图片,找到脚本修改idir:属性改成自己的图片文件夹存放路径即可. asp.net mvc3提交内容报错提示含 ...

  10. rabibtMQ安装及集群配置-linux

    安装RabbitMQ RabbitMQ是流行的开源消息队列系统,用erlang语言开发,故首先需要安装erlang依赖及erlang. 安装erlang依赖的基本环境,通过yum方式进行安装: yum ...