SyncRequestProcessor事务日志记录处理器

来源:互联网 发布:无线网卡隐藏mac地址 编辑:程序博客网 时间:2024/05/17 00:41

概括

SyncRequestProcessor主要用来将事务请求记录到事务日志文件中,同时还会在合适时候触发zookeeper进行数据快照。

SyncRequestProcessor是个异步处理的processor,启动后会有一个线程异步获取队列queuedRequests中的request并处理

SyncRequestProcessorr负责把事务请求(写request)持久化到本地磁盘,为了提高写磁盘的效率,这里使用的是缓冲写,通过周期性(1000个request)的调用flush操作,flush之后request已经确保写到磁盘了,这时会把请求传给AckRequestProcessor或SendAckRequestProcessor继续处理。使用这种方式可以在持久化多个事务的时候,只使用一次磁盘寻道(Disk Seek)的开销。Request对象只有在其中事务同步到磁盘后(flush后),才会传递到下一个处理器。

这个processor除了会定期的把request持久化到本地磁盘,同时他还要维护本机的txnlog和snapshot,这里的基本逻辑是:
每隔snapCount/2个request会重新生成一个snapshot并滚动一次txnlog,同时为了避免所有的zookeeper server在同一个时间生成snapshot和滚动日志,这里会再加上一个随机数,snapCount的默认值是10w个request

生成镜像是通过扫描保存在内存中的Datatree和sessionTracker来把状态持久化的,为了提高server的性能,在生成snapshot的时候并未对Datatree和sessionTracker加锁,这里就涉及到保存状态一致的问题,所以我们可以称它为“模糊快照”,没关系,因为我们有每个事务的日志,我们可以通过“快照”+”日志“恢复。

SyncRequestProcessor被用于下面三种不同的情景中:

  1. Leader - 同步请求到磁盘,并且转发这个请求到AckRequestProcessor。该处理器发送Ack消息给Leader自己。
  2. Follower -同步请求到磁盘,并转发请求到SendAckRequestProcessor。该处理器发送确认数据包给Leader。SendAckRequestProcessor是flushable,允许我们强制将数据包推送到Leader。
  3. Observer -同步已经提交的请求到磁盘(从INFORM数据包中接收)。它不会发送确认数据包给Leader。所以nextProcessor是null。在observer中,和一般的txnlog语义不同,因为它仅包含已经提交的txn。

在SyncRequestProcessor中有两个关键队列:

  1. queuedRequest队列:存放从传入该处理器的Request对象。当调用该处理器的processRequest方法,会将Request对象放入到queuedRequest队列;
  2. toFlush队列:存放已经附加到日志文件,但还没有Flush的Request对象。
    SyncRequestProcessor的run方法循环读取queuedRequests队列中的Request对象并进行持久化。

流程图如下:

这里写图片描述

如果toFlush队列为空,则调用queuedRequest队列的阻塞方法take();如果toFlush队列不为空,则调用queuedRequest队列的非阻塞方法poll()。如果poll()方法返回null,则会立即将toFlush队列中所有Request对象中事务Flush到磁盘,并将Request对象传入到下一个处理器。这样可以避免增加请求处理的延时。如果queuedRequest.poll()方法返回不为Null或者queuedRequest.take()方法返回, 则将返回的Request对象si中的事务附加到事务日志文件中,并放入toFlush队列中。如果toFlush队列大小大于1000,则将队列中所有Request对象中事务Flush到磁盘,并将Request对象传入下一个处理器。这是可以避免在有大量请求的时候增加请求处理的延时。

Request对象附加到事务日志之后,会检查日志记录数logCount是否大于(snapCount / 2 + randRoll)。如果大于则滚动日志,并启动生成新Snapshot的线程。其中randRoll是一个随机数。这个随机数的使用可以避免Zookeeper集群里的所有机器同时构建Snapshot。

源码分析如下

public void run() {    try {        int logCount = 0;       //这个随机数randRoll的使用可以避免Zookeeper集群里的所有机器同时构建Snapshot        setRandRoll(r.nextInt( snapCount/2));        while (true ) {            Request si = null;            //如果toFlush为空,则调用队列queuedRequests的阻塞方法take()            if (toFlush .isEmpty()) {                si = queuedRequests.take();            }                            //如果toFlush不为空,则调用队列queuedRequests的非阻塞方法poll()             else {                si = queuedRequests.poll();                //如果si为null, 说明queuedRequests为空,则调用flush()方法                if (si == null) {                    flush( toFlush);                    continue;                }            }            //如果si是一个poison pill, 则退出循环            if (si == requestOfDeath ) {                break;            }            if (si != null) {                // track the number of records written to the log                //将record的操作记到日志中                if (zks .getZKDatabase().append(si)) {                    logCount++;                    if (logCount > (snapCount / 2 + randRoll)) {                        randRoll = r .nextInt(snapCount/2);                        //滚动事务日志                        zks.getZKDatabase().rollLog();                        //构建snapshot                        if (snapInProcess != null && snapInProcess.isAlive()) {                            LOG.warn("Too busy to snap, skipping" );                        } else {                            //生成snapshot线程                            snapInProcess = new Thread("Snapshot Thread") {                                    public void run() {                                        try {                                            zks.takeSnapshot();                                        } catch(Exception e) {                                            LOG.warn("Unexpected exception", e);                                        }                                    }                                };                            //启动snapInProcess                            snapInProcess.start();                        }                        logCount = 0;                    }                }                else if (toFlush .isEmpty()) {                    // optimization for read heavy workloads                    //如果这是一个read(不是事务), 并且没有pending的flushes(writes), 那么直接传递到下一个处理器                    if (nextProcessor != null) {                        nextProcessor.processRequest(si);                        if (nextProcessor instanceof Flushable) {                            ((Flushable) nextProcessor).flush();                        }                    }                    continue;                }                toFlush.add(si);                //如果toFlush的大小大于1000, 则flush                if (toFlush .size() > 1000) {                    flush( toFlush);                }            }        }    } catch (Throwable t) {        LOG.error("Severe unrecoverable error, exiting" , t);        running = false ;        System. exit(11);    }    LOG.info("SyncRequestProcessor exited!" );}
原创粉丝点击