SyncRequestProcessor作为一个ZooKeeper中的一个关键线程(ZooKeeperCriticalThread),是ZooKeeper请求处理链中的事务日志记录处理器,其主要用来将事务请求记录到事务日志文件中去,同时还会触发ZooKeeper进行数据快照。

数据结构

  • LinkedBlockingQueue<Request> queuedRequests:上一个RequestProcessor调用nextProcessor.processRequest(request)将request排入该队列中等待处理。
  • Thread snapInProcess:负责快照线程,保证数据快照过程不影响ZooKeeper的主流程,需创建一个单独的异步线程来进行数据快照。
  • LinkedList<Request> toFlush:在持久化过程中,使用组提交(Group Commits)来优化磁盘I/O操作。想象一个场景:当客户端有大量的事务请求,如果每次写请求都同步到磁盘,那么性能就会产生问题。所以设置该链表来暂存需要持久化到磁盘的Request。
  • int snapCount:默认为100000,表示ZooKeeper每隔snapCount次事务日志记录后进行一个数据快照。

toFlush以及flush时机

  toFlush队列可用于存储请求,可能是读也可能是写。

  ZooKeeper专门使用线程SyncRequestProcessor来处理请求,所以这个线程必须合理的工作,否则会对整体的性能造成影响。如果客户端请求为读请求就没必要进行flush了,但如果是写请求,就必须把请求写入log,这个写入未必能保证真的同步到磁盘。所以合适的时机将缓存的事务日志刷入到磁盘是必须的。

  从程序的设计应该能看到作者出于这个考虑选择了两个时机来做这件事情:

  • 如果没有请求的时候(即较空闲的时候)
  • 如果一直繁忙,则toFlush队列到达了一定数量(1000),就会批量同步

注意点

  • 数据快照

    每进行一次事务日志记录之后,ZooKeeper都会检测当前是否需要进行数据快照。理论上进行snapCount次事务操作后就会开始数据快照,但是考虑到数据快照对于ZooKeeper所在机器的整体性能影响,需要尽量避免ZooKeeper集群中所有机器在同一时刻进行数据快照。因此ZooKeeper在具体的实现中,并不是严格按照这个策略执行,而是采取“过半随机”策略,即符合如下条件就进行数据快照:

      logCount > (snapCount / 2 + randRoll)

    其中logCount代表了当前已经记录的事务日志数量,randRoll1 ~ snapCount/2之间的随机数,因此上面的条件就相当于:如果我们配置的snapCount100000,那么ZooKeeper会在50000 ~ 100000次事务日志记录后进行一次数据快照。

  • 事务日志文件切换

    当满足上述条件时,ZooKeeper就要开始进行数据快照了。首先是进行事务日志文件的切换。所谓的事务日志文件切换时指当前的事务日志已经“写满”,需要重新创建一个新的事务日志。即每当进行一次数据快照,重新创建一个事务日志文件。

源码

int logCount = 0;
int randRoll = r.nextInt(snapCount/2); // 产生0~snapCount/2之间的随机数
while (true) {
Request si = null;
if (toFlush.isEmpty()) {
si = queuedRequests.take(); // toFlush为空,不需要flush,没有数据则直接阻塞掉
} else {
si = queuedRequests.poll(); // 没有数据直接返回,有则拿出
if (si == null) { // 如果queuedRequests中没有数据,但toFlush不空,则表明ZooKeeper现在比较空闲,可以进行flush
flush(toFlush);
continue;
}
}
if (si == requestOfDeath) {
break;
}
if (si != null) {
if (zks.getZKDatabase().append(si)) { // 如果si是事务请求
logCount++;
if (logCount > (snapCount / 2 + randRoll)) { // 满足条件,需要进行数据快照
randRoll = r.nextInt(snapCount/2);
zks.getZKDatabase().rollLog(); // 切换事务日志文件 if (snapInProcess != null && snapInProcess.isAlive()) {
LOG.warn("Too busy to snap, skipping");
} else {
snapInProcess = new ZooKeeperThread("Snapshot Thread") { // 创建数据快照异步线程
public void run() {
try {
zks.takeSnapshot();
} catch(Exception e) {
LOG.warn("Unexpected exception", e);
}
}
};
snapInProcess.start(); // 保证数据快照过程不影响ZooKeeper的主流程,创建一个单独的异步线程来进行数据快照
}
logCount = 0;
}
} else if (toFlush.isEmpty()) { // 如果是非事务请求(读操作)且toFlush为空
// 说明近一段时间读多写少,直接响应,此处优化为了读比较频繁操作
// 为何读写不分开???
if (nextProcessor != null) {
nextProcessor.processRequest(si);
if (nextProcessor instanceof Flushable) {
((Flushable)nextProcessor).flush();
}
}
continue; // nextProcessor已经对该si处理过了,不用添加到toFlush中
}
toFlush.add(si);
if (toFlush.size() > 1000) { // 超过1000,直接flush
flush(toFlush);
}
}
}
参考

ZooKeeper事务日志记录器SyncRequestProcessor的更多相关文章

  1. zookeeper 事务日志

    前面提到,在zookeeper server的配置文件zoo.cfg中可以通过dataLogDir来配置zookeeper的事务日志的输出目录,这个事务日志类似于下面这样的文件: 这个文件是一个二进制 ...

  2. zookeeper 事务日志与快照日志

    zookeeper日志各类日志简介 zookeeper服务器会产生三类日志:事务日志.快照日志和log4j日志. 在zookeeper默认配置文件zoo.cfg(可以修改文件名)中有一个配置项data ...

  3. zookeeper 事务日志查看

    在version下的日志是二进制文件,查看需要转换 创建/data/middleware/zookeeper-3.4.14/translog.sh 脚本 格式化命令: java -classpath ...

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

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

  5. zookeeper读取事务日志、快照日志

    zookeeper的事务日志的格式如 log.xxx, xxx表示顺序序号 我使用的zookeeper版本:3.5.5 事务日志 执行命令 java -cp .:/tmp/zookeeper-3.5. ...

  6. ZooKeeper系列(5):ZooKeeper的日志和快照

    ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk ZooKeeper有两种日志.一种快照.日志分为事务日志和Zoo ...

  7. SharePoint 2010 数据库xxx的事务日志已满

    接到领导安排,说客户有问题 请求协助解决,对方给我展示的错误日志,如下: 数据库'WSS_Content_xxxx'的事务日志已满.若要查明无法重用日志中的空间的原因,请参阅sy.databases中 ...

  8. SQL Server 事务以及事务日志综述

    事务是一个非常重要的概念,特此在这里写一些文章来总结.整篇文章还在持续更新中. 在本系列文章中,你将看到以下内容: 数据库事务(Database Transaction)概述 事务操作(BEGIN/C ...

  9. SQL Server 事务日志传输

    概述 可以使用日志传送将事务日志不间断地从一个数据库(主数据库)发送到另一个数据库(辅助数据库).不间断地备份主数据库中的事务日志,然后将它们复制并还原到辅助数据库,这将使辅助数据库与主数据库基本保持 ...

随机推荐

  1. vertica 8.0 新特性

    前言: <<line>> 表明在vertica 8.0文档中的title   正文: 1-支持平台 1.1-操作系统 <<Vertica Server and Ve ...

  2. WEB框架

     WEB框架本质                                                                        一.WEB请求流程 所有的web应用,都 ...

  3. WebKit的CSS扩展(WebKit是私有属性)

    http://www.css88.com/webkit/-webkit-touch-callout/ -webkit-tap-highlight-color 是一个 不规范的属性(unsupporte ...

  4. Comet4J推技术在SSHE三大框架中应用-linux下亲测可用

    Comet4J(Comet for Java)是一个纯粹基于AJAX(XMLHTTPRequest)的服务器推送框架,消息以JSON方式传递,具备长轮询.长连接.自动选择三种工作模式. 下载地址 co ...

  5. Django中载入javascript、css的操作

    url.py中的设置:  导入模块 from django.conf import settings (r'^js/(?P<path>.*)$','django.views.static. ...

  6. 深入理解js——作用域

    "javaScript没有块级作用域",所谓"块",就是{}中间的内容.所以在声明变量的时候不要在"块"里面,要在一开始声明就好了. 其实j ...

  7. sql 事物以及回滚

    第一种: Begin Try    Begin Tran Tran1        insert into t1(Id, c1) values(1,'1')        insert into t1 ...

  8. Sharif University CTF 2016 -- Login to System (PWN 200)

    EN: It's easy to find out where is the bug : .text:0000000000400DE4 ; void *start_routine(void *).te ...

  9. Android中的事件传递机制

    Android源码版本:API Level 19(Android 4.4) Android事件构成 在Android中,事件主要包括点按.长按.拖拽.滑动等,点按又包括单击和双击,另外还包括单指操作和 ...

  10. JavaScript内置对象之数组

    一.JavaScript对象之数组 1.创建数组的方式 (1)使用Array构造函数 语法:new Array() 小括号()说明: -预先知道数组要保存的项目数量 -向Array构造函数中传递数组应 ...