Zookeeper(四))持久化日志文件

持久化用途

存储两种文件

  • snapshot:内存快照
  • log:事务日志,类似MySQL的binlog,存储数据节点的操作日志

问题

  • 序列化的本质其实就是将原数据重新写入
  • roll中的BufferedOutputStream.flush和commit中的FileChannel.force()都是强制刷新:有什么区别

基本术语

  • FileTxnSnapLog,封装了TxnLog和SnapShot。 是操作数据文件和快照文件的对外API
  • TxnLog,接口类型,读取事务性日志的接口。
  • FileTxnLog,实现TxnLog接口,添加了访问该事务性日志的API。
  • Snapshot,接口类型,持久层快照接口。
  • FileSnap,实现Snapshot接口,负责存储、序列化、反序列化、访问快照。
  • Util,工具类,提供持久化所需的API。

实现

TxnLog
public interface TxnLog {
/**
* Setter for ServerStats to monitor fsync threshold exceed
* @param serverStats used to update fsyncThresholdExceedCount
*/
void setServerStats(ServerStats serverStats); // 滚动日志,从当前日志滚到下一个日志,不是回滚
void rollLog() throws IOException;
// 添加一个请求至事务性日志
boolean append(TxnHeader hdr, Record r) throws IOException; // 读取事务性日志
TxnIterator read(long zxid) throws IOException; // 事务性操作的最新zxid
long getLastLoggedZxid() throws IOException; // 清空zxid以后的日志
boolean truncate(long zxid) throws IOException; // 获取数据库的id
long getDbId() throws IOException; // 提交事务并进行确认
void commit() throws IOException; long getTxnLogSyncElapsedTime(); // 关闭事务性日志
void close() throws IOException;
}
FileTxnLog

实现TxnLog接口,提供操作事务日志的api

public class FileTxnLog implements TxnLog {
//最新的日志zxid
long lastZxidSeen;
//日志文件
volatile BufferedOutputStream logStream = null;
volatile OutputArchive oa;
//日志存储文件
File logFileWrite = null;
private FilePadding filePadding = new FilePadding();
private LinkedList<FileOutputStream> streamsToFlush =
new LinkedList<FileOutputStream>();
//重置日志文件
public synchronized void rollLog() throws IOException {
if (logStream != null) {
this.logStream.flush();
this.logStream = null;
oa = null;
}
}
//添加事务日志 hdr:事务标题 txn:事务本身
public synchronized boolean append(TxnHeader hdr, Record txn)
throws IOException {
if (hdr == null) {
return false;
}
//判断并更新最新的zxid
if (hdr.getZxid() <= lastZxidSeen) {
LOG.warn("Current zxid " + hdr.getZxid()
+ " is <= " + lastZxidSeen + " for " + hdr.getType());
} else {
lastZxidSeen = hdr.getZxid();
}
//构建事务日志文件
if (logStream==null) {
if(LOG.isInfoEnabled()){
LOG.info("Creating new log file: " + Util.makeLogName(hdr.getZxid()));
}
//1. 生成新的log文件
logFileWrite = new File(logDir, Util.makeLogName(hdr.getZxid()));
//2. 生成log文件输出流
fos = new FileOutputStream(logFileWrite);
//为写入给定的输出流而创建缓冲输出流
logStream=new BufferedOutputStream(fos);
//获取二进制序列化类 TODO
//BinaryOutputArchive内部维护一个DataOutput 根据值传递特性
//oa序列化写入时其实就是写入log文件
oa = BinaryOutputArchive.getArchive(logStream);
//3. 用TXNLOG_MAGIC VERSION dbId来生成事务日志文件头
FileHeader fhdr = new FileHeader(TXNLOG_MAGIC,VERSION, dbId);
//4. 将事务日志文件头序列化到文件上
fhdr.serialize(oa, "fileheader");
//确保文件扩展之前 魔数已被写入
logStream.flush();
filePadding.setCurrentSize(fos.getChannel().position());
streamsToFlush.add(fos);
}
//5. 剩余空间不足时 填充文件
filePadding.padFile(fos.getChannel());
//6. 将事务头部和本身序列化为字节数组
byte[] buf = Util.marshallTxnEntry(hdr, txn);
if (buf == null || buf.length == 0) {
throw new IOException("Faulty serialization for header " + "and txn");
}
//生成验证算法 校验数据流
Checksum crc = makeChecksumAlgorithm();
crc.update(buf, 0, buf.length);
oa.writeLong(crc.getValue(), "txnEntryCRC");
//6. 将当前序列化的事务记录写入到oa
Util.writeTxnBytes(oa, buf);
return true;
}
//从给定的zxid开始读取日志文件
public TxnIterator read(long zxid, boolean fastForward) throws IOException {
return new FileTxnIterator(logDir, zxid, fastForward);
}
//提交日志 保存到磁盘
public synchronized void commit() throws IOException {
//刷到磁盘
if (logStream != null) {
logStream.flush();
}
//强刷到磁盘
for (FileOutputStream log : streamsToFlush) {
log.flush();
if (forceSync) {
long startSyncNS = System.nanoTime();
FileChannel channel = log.getChannel();
//会强制将所有未写入磁盘的数据都强制写入磁盘 比如还在缓冲区中的数据
channel.force(false);
syncElapsedMS = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startSyncNS);
if (syncElapsedMS > fsyncWarningThresholdMS) {
if(serverStats != null) {
serverStats.incrementFsyncThresholdExceedCount();
}
LOG.warn("fsync-ing the write ahead log in "
+ Thread.currentThread().getName()
+ " took " + syncElapsedMS
+ "ms which will adversely effect operation latency. "
+ "File size is " + channel.size() + " bytes. "
+ "See the ZooKeeper troubleshooting guide");
}
ServerMetrics.FSYNC_TIME.add(syncElapsedMS);
}
}
//移除流并关闭
while (streamsToFlush.size() > 1) {
streamsToFlush.removeFirst().close();
}
//当日志文件大小超过限制 强刷到磁盘并重置
if(txnLogSizeLimit > 0) {
long logSize = getCurrentLogSize();
if (logSize > txnLogSizeLimit) {
LOG.debug("Log size limit reached: {}", logSize);
rollLog();
}
}
}
}

FileTxnIterator:用于读取事务日志

public static class FileTxnIterator implements TxnLog.TxnIterator {
public FileTxnIterator(File logDir, long zxid, boolean fastForward)
throws IOException {
this.logDir = logDir;
this.zxid = zxid;
init();
if (fastForward && hdr != null) {
while (hdr.getZxid() < zxid) {
if (!next())
break;
}
}
}
void init() throws IOException {
storedFiles = new ArrayList<File>();
//找出大于等于snapshot中最大的zxid的logfile及后续logfile并升序
List<File> files = Util.sortDataDir(FileTxnLog.getLogFiles(logDir.listFiles(), 0), LOG_FILE_PREFIX, false);
for (File f: files) {
if (Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX) >= zxid) {
storedFiles.add(f);
}
// add the last logfile that is less than the zxid
else if (Util.getZxidFromName(f.getName(), LOG_FILE_PREFIX) < zxid) {
storedFiles.add(f);
break;
}
}
goToNextLog();
next();
}
}

Zookeeper(四))持久化日志文件的更多相关文章

  1. MySQL5.7 四种日志文件

    mysql 日志包括:错误日志,二进制日志,通用查询日志,慢日志等 一:通用查询日志: 记录建立的客户端连接和执行的语句 1)show variables like '%verision%'; 显示数 ...

  2. Zookeeper日志文件&事务日志&数据快照

    Zookeeper持久化两类数据,Transaction以及Snapshot,logDir存储transaction命令,dataDir存储snap快照,其下子目录名称以version-2命名,子目录 ...

  3. zookeeper清除日志文件工具

    zookeeper运行时间长了以后,日志会成为一个比较大的问题.比如作者压力测试hbase一周以后,zookeeper日志文件达到了10G的规模.由于zookeeper日志文件不能随意删除,因为一个长 ...

  4. 消费滚动滴log日志文件(flume监听,kafka消费,zookeeper协同)

    第一步:数据源 手写程序实现自动生成如下格式的日志文件: 15837312345,13737312345,2017-01-09 08:09:10,0360 打包放到服务器,使用如下命令执行,模拟持续不 ...

  5. 四步搞定Zabbix 日志文件监控

    Zabbix 日志文件监控 一.给运行Zabbix agent的用户授予要监控日志的读取权限. 1. 執行下面的命令,追加app的可讀權限: setfacl -m u:app:r-- /var/log ...

  6. nginx(四)初识nginx日志文件

    nginx 日志相关指令主要有两条,一条是log_format,用来设置日志格式,另外一条是access_log,用来指定日志文件的存放路径.格式和缓存大小,通俗的理解就是先用log_format来定 ...

  7. 【lucene系列学习四】log4j日志文件实现多线程的测试

    参考资料:http://nudtgk2000.iteye.com/blog/1716379 首先,在http://www.apache.org/dyn/closer.cgi/logging/log4j ...

  8. ELK+Filebeat+Kafka+ZooKeeper 构建海量日志分析平台(elk5.2+filebeat2.11)

    ELK+Filebeat+Kafka+ZooKeeper 构建海量日志分析平台 参考:http://www.tuicool.com/articles/R77fieA 我在做ELK日志平台开始之初选择为 ...

  9. zookeeper(3) 持久化

    zookeeper为了防止,系统宕机或重启导致的数据丢失,会对数据进行定时持久化.有两种持久化方式: 1.为每次事务操作记录到日志文件,这样就可以通过执行这些日志文件来恢复数据. 2.为了加快ZooK ...

随机推荐

  1. server001

  2. 第二章:排序算法 及其他 Java代码实现

    目录 第二章:排序算法 及其他 Java代码实现 插入排序 归并排序 选择排序算法 冒泡排序 查找算法 习题 2.3.7 第二章:排序算法 及其他 Java代码实现 --算法导论(Introducti ...

  3. 分布式的几件小事(四)dubbo负载均衡策略和集群容错策略

    1.dubbo负载均衡策略 ①random loadbalance 策略 默认情况下,dubbo是random loadbalance 随机调用实现负载均衡,可以对provider不同实例设置不同的权 ...

  4. Javascript问题集锦

    1.Date.parse()函数兼容性问题:   IE Chrome Firefox Date.parse("07-17-2019") 1563292800000 15632928 ...

  5. openlayers之全屏控件的使用

    import { FullScreen } from 'ol/control' map.addControl(new FullScreen())

  6. 好用的 python 工具集合

    图标处理小程序, 妈妈再也不用担心我不会制作图标了 # PythonMargick包可以到Unofficial Windows Binaries for Python Extension Packag ...

  7. Nginx的快速安装

    1. 准备工作 1. CenterOS7.x.vmware虚拟机,安装过程参考 https://jingyan.baidu.com/article/eae0782787b4c01fec548535.h ...

  8. 数据绘图工具之Matplotlib

    一.安装:绘图和可视化 pip install matplotlib 我们已经下好了anaconda 包含了绘图工具包 直接导入即可 import matplotlib.pyplotlib as pl ...

  9. 防范DDoS攻击的15个方法

    0x01 背景 为了对抗 DDoS(分布式拒绝服务)攻击,你需要对攻击时发生了什么有一个清楚的理解..简单来讲,DDoS 攻击可以通过利用服务器上的漏洞,或者消耗服务器上的资源(例如 内存.硬盘等等) ...

  10. Web前端开发解耦1

    在网站建设的工作中,Web前端工程师占据着非常重要的位置,好的前端工程师保证了良好的网站优化以及友好的用户体验.今天佚站互联主要分享一下对于Web前端开发规范的一些见解. 学过面向对象编程的朋友应该都 ...