概述

从节点参与选举,选举结束,自身不为主,自动成为集群从节点。
从节点,一方面作为集群主节点的从节点,与其交互。
一方面,从节点可以作为集群观察者的主节点,与观察者交互。

与主交互

主干逻辑–followLeader

设置Zab状态为DISCOVERY
寻找主节点
向主节点注册
	包的类型为Leader.FOLLOWERINFO
	包的zxid为acceptedEpoch&0
	一个LearnerInfo对象,包含自身集群id,0x10000,自身集群版本
	发送包
	收取回复
	回复包的zxid中取得newEpoch
	如果回复包类型为Leader.LEADERINFO
		协议版本
		新的newEpoch大于自身acceptedEpoch
			自身acceptedEpoch采取newEpoch
			将自身currentEpoch设置到wrappedEpochBytes
		新的newEpoch和自身acceptedEpoch一致
			将-1设置到wrappedEpochBytes
		回复包
			包的类型Leader.ACKEPOCH
				自身数据实体得到的lastLoggedZxid
				epochBytes
				return ZxidUtils.makeZxid(newEpoch, 0)
	如果回复包的类型不是Leader.ACKEPOCH
		新的newEpoch大于自身acceptedEpoch
			自身acceptedEpoch采取newEpoch
			return qp.getZxid();
设置Zab状态为SYNCHRONIZATION
与主节点同步
	
设置Zab状态为BROADCAST
如果自身面向观察者监听端口配置了,
	om = new ObserverMaster(self, fzk, self.getObserverMasterPort());
    om.start();
循环迭代
	读取包
	处理包

处理包

包的类型为Leader.PING
	从zk服务对象得到touchTable
	序列化touchTable
	回复包
		包类型Leader.PING
		包zxid--和收取包中一致
		数据实体--序列化数据
		授权信息
包的类型为Leader.PROPOSAL
	反向序列化得到TxnLogEntry对象,包含事务头,事务体,事务摘要
	记录zxid到lastQueued
	fzk.logRequest(hdr, txn, digest);
		Request request = new Request(hdr.getClientId(), hdr.getCxid(), hdr.getType(), hdr, txn, hdr.getZxid());
    	request.setTxnDigest(digest);
    	if ((request.zxid & 0xffffffffL) != 0) 
    	{
        	pendingTxns.add(request);
    	}
    
    	// 提议交由SyncProcessor--AckProcess
    	syncProcessor.processRequest(request);
	如果面向观察者充当观察者主节点
		om.proposalReceived(qp);
包的类型为Leader.COMMIT
	fzk.commit(qp.getZxid());
	如果面向观察者充当观察者主节点
		om.proposalCommitted(qp.getZxid());
包的类型为Leader.UPTODATE
	错误日志
包的类型为Leader.REVALIDATE
	if (om == null || !om.revalidateLearnerSession(qp)) 
    {
            revalidate(qp);
    }
包的类型为Leader.SYNC
	fzk.sync();

syncWithLeader–与主同步

读取包
包的类型为Leader.DIFF
	设置同步模式为DIFF
	if (zk.shouldForceWriteInitialSnapshotAfterLeaderElection()) 
    {
                snapshotNeeded = true;
                syncSnapshot = true;
    } 
    else 
    {
                snapshotNeeded = false;
    }
包的类型为Leader.SNAP
	设置同步模式为SNAP
	反向序列化执行快照恢复
	zk.getZKDatabase().setlastProcessedZxid(qp.getZxid());
    syncSnapshot = true;
包的类型为Leader.TRUNC
	设置同步模式为TRUNC
	boolean truncated = zk.getZKDatabase().truncateLog(qp.getZxid());
	zk.getZKDatabase().setlastProcessedZxid(qp.getZxid());
包的类型为其他
	未知类型报错
zk.getZKDatabase().initConfigInZKDatabase(self.getQuorumVerifier());
zk.createSessionTracker();
long lastQueued = 0;
boolean isPreZAB1_0 = true;				
写快照,写事务日志是互斥的标志
循环迭代:
	读取包
	包的类型为Leader.PROPOSAL
		反向序列化得到PacketInFlight
		记录zxid到lastQueued
		加入到packetsNotCommitted
	包的类型为Leader.COMMIT
		从packetsNotCommitted查看首个
		需要快照
			zk.processTxn(packet.hdr, packet.rec);
		不需要快照
			packetsNotCommitted.add(packet);
            packetsCommitted.add(qp.getZxid());
    包的类型为Leader.UPTODATE
    	isPreZAB1_0为true	
    		zk.takeSnapshot(syncSnapshot);
            self.setCurrentEpoch(newEpoch);
        self.setZooKeeperServer(zk);
        self.adminServer.setZooKeeperServer(zk);
        跳出循环
    包的类型为Leader.NEWLEADER
    	需要快照
    		zk.takeSnapshot(syncSnapshot);
    	设置currentEpoch为newEpoch
    	writeToTxnLog = true;
        isPreZAB1_0 = false;
        设置同步模式为NONE
        zk.startupWithoutServing();
        如果是从节点
        	FollowerZooKeeperServer fzk = (FollowerZooKeeperServer) zk;
            for (PacketInFlight p : packetsNotCommitted) 
            {
                        fzk.logRequest(p.hdr, p.rec, p.digest);
            }
                    
            packetsNotCommitted.clear();
       发送回复
       		包的类型Leader.ACK
       		包的zxid为newLeaderZxid
回复包
	包类型Leader.ACK
	包的zxid为newEpoch&0
zk.startServing();
self.updateElectionVote(newEpoch);
如果角色是集群从节点
	FollowerZooKeeperServer fzk = (FollowerZooKeeperServer) zk;
    for (PacketInFlight p : packetsNotCommitted) 
    {
            fzk.logRequest(p.hdr, p.rec, p.digest);
    }
        
    for (Long zxid : packetsCommitted) 
    {
            fzk.commit(zxid);
    }

面向外部客户提供服务

请求处理链

FollowerRequestProcessor
CommitProcessor
FinalRequestProcessor

一个独立与以上体系的
SyncRequestProcessor
SendAckRequestProcessor

FollowerRequestProcessor–线程体

循环迭代
	从queuedRequests获取请求对象
	if (skipLearnerRequestToNextProcessor && request.isFromLearner()) 
    {
            ServerMetrics.getMetrics().SKIP_LEARNER_REQUEST_TO_NEXT_PROCESSOR_COUNT.add(1);
    } 
    else 
    {
            nextProcessor.processRequest(request);
    }
    如果请求类型为OpCode.sync
    	zks.pendingSyncs.add(request);
        zks.getFollower().request(request);---将请求打包发送给主节点
    如果请求类型为OpCode.create/OpCode.create2/OpCode.createTTL/OpCode.createContainer:
    OpCode.delete/OpCode.deleteContainer/OpCode.setData/OpCode.reconfig/OpCode.setACL:
    OpCode.multi/OpCode.check:
    	zks.getFollower().request(request);
    如果请求类型为OpCode.createSession/OpCode.closeSession:
    	if (!request.isLocalSession()) 
        {
                        zks.getFollower().request(request);
        }

processRequest—处理来自其下观察者发来的请求,处理来自外部客户发来的请求

if (checkForUpgrade) 
{
    Request upgradeRequest = null;
    try 
    {
        upgradeRequest = zks.checkUpgradeSession(request);
    } 
    catch (KeeperException ke) 
    {
        if (request.getHdr() != null) 
        {
            request.getHdr().setType(OpCode.error);
            request.setTxn(new ErrorTxn(ke.code().intValue()));
        }
        
        request.setException(ke);
        LOG.warn("Error creating upgrade request", ke);
    } 
    catch (IOException ie) 
    {
        LOG.error("Unexpected error in upgrade", ie);
    }
    
    if (upgradeRequest != null) 
    {
        queuedRequests.add(upgradeRequest);
    }
}

queuedRequests.add(request);

}

CommitProcessor–线程体

循环迭代
	阻塞等待committedRequests或queuedRequests非空
循环迭代
	如果queuedRequests为空,或达到批处理次数,跳出
	从queuedRequests取出请求对象
	请求需要提交,则加入pendingRequests。
		存入sid--sid下顺序等待请求对象集合。
	请求所属sid,有请求在pendingRequests中,
		存入sid--sid下顺序等待请求对象集合。
	请求不需提交,且所属sid无请求在pendingRequests中,
		直接将请求发给下一处理阶段处理

等待没有请求处于提交中。
循环迭代:
	从committedRequests中取出请求A
	如果queuedWriteRequests非空,且queuedWriteRequests中首个请求的sid,cxid均与取出请求A匹配
	pendingRequests中取出A所属会话的排队请求容器B
	对排队请求容器B中需要提交的请求,
		取出此请求,queuedWriteRequests移除首个请求
		queuesToDrain加入此请求的sid
	committedRequests移除请求A
	对请求A执行processWrite

对queuesToDrain中每个sid
	取得sid下所有排队等待请求,
	从头顺序对每个读请求,提交给下一级处理

sendToNextProcessor

临时增加numRequestsProcessing
请求排队待处理
实际处理为:
	流水线下一级处理
	递减numRequestsProcessing
	减少为0,执行唤醒

processWrite

流水线下一级处理

committedRequests来源

来自主节点

本级请求处理

queuedRequests中加入请求
若请求需要提交
	queuedWriteRequests中加入请求

FinalRequestProcessor

对请求执行applyRequest
获取请求的ServerCnxn
获取数据实体lastProcessedZxid
如果请求的类别为OpCode.ping
	最后操作记录为PING
	更新状态【请求,最后op,lastZxid】
	通过ServerCnxn发送回复
		回复类别为ClientCnxn.PING_XID
		回复zxid为lastProcessedZxid
如果请求的类别为OpCode.createSession
	最后操作记录为SESS
	更新状态【请求,最后op,lastZxid】
	完成会话初始化,体现为发送回复
		包含版本,会话超时,会话id,会话密码
如果请求的类别为OpCode.multi
	最后操作记录为MULT
	动态构造MultiResponse对象
	对rc.multiResult中每个ProcessTxnResult
		如果ProcessTxnResult的类别为OpCode.check,
			构造新的CheckResult对象
		如果ProcessTxnResult的类别为OpCode.create,
			构造新的CreateResult(subTxnResult.path);对象
		如果ProcessTxnResult的类别为OpCode.create2/OpCode.createTTL/OpCode.createContainer
			构造新的CreateResult(subTxnResult.path, subTxnResult.stat);对象
		如果ProcessTxnResult的类别为OpCode.delete/OpCode.deleteContainer
			构造新的DeleteResult对象
		如果ProcessTxnResult的类别为OpCode.setData
			构造新的SetDataResult(subTxnResult.stat)对象
		如果ProcessTxnResult的类别为OpCode.error
			构造新的new ErrorResult(subTxnResult.err);
		
		((MultiResponse) rsp).add(subResult);
如果请求的类别为OpCode.multiRead
	最后操作记录为MLTR
	动态构造新的new MultiOperationRecord()
	反向序列化填充MultiOperationRecord对象
	动态构造rsp = new MultiResponse();对象
	对multiReadRecord的每个Op
		如果类型为OpCode.getChildren
			rec = handleGetChildrenRequest(readOp.toRequestRecord(), cnxn, request.authInfo);
            subResult = new GetChildrenResult(((GetChildrenResponse) rec).getChildren());
		如果类型为OpCode.getData
			rec = handleGetDataRequest(readOp.toRequestRecord(), cnxn, request.authInfo);
            GetDataResponse gdr = (GetDataResponse) rec;
            subResult = new GetDataResult(gdr.getData(), gdr.getStat());
如果请求的类别为OpCode.create
	最后操作记录为CREA
	动态构造new CreateResponse(rc.path)
	requestPathMetricsCollector.registerRequest(request.type, rc.path);
如果请求的类别为OpCode.create2/OpCode.createTTL/OpCode.createContainer
	最后操作记录为CREA
	动态构造new Create2Response(rc.path, rc.stat)
	requestPathMetricsCollector.registerRequest(request.type, rc.path)
如果请求的类别为OpCode.delete/OpCode.deleteContainer
	最后操作记录为DELE
	requestPathMetricsCollector.registerRequest(request.type, rc.path);
如果请求的类别为OpCode.setData
	最后操作记录为SETD
	动态构造new SetDataResponse(rc.stat)
	requestPathMetricsCollector.registerRequest(request.type, rc.path);
如果请求的类别为OpCode.reconfig
	最后操作记录为RECO
	动态构造new GetDataResponse(((QuorumZooKeeperServer) zks).self.getQuorumVerifier().toString().getBytes(UTF_8), rc.stat);
如果请求的类别为OpCode.setACL
	最后操作记录为SETA
	动态构造new SetACLResponse(rc.stat)
	requestPathMetricsCollector.registerRequest(request.type, rc.path);
如果请求的类别为OpCode.closeSession
	最后操作记录为CLOS
如果请求的类别为OpCode.sync
	最后操作记录为SYNC
	动态构造new SyncRequest()
	反向序列化
	rsp = new SyncResponse(syncRequest.getPath());
    requestPathMetricsCollector.registerRequest(request.type, syncRequest.getPath());
如果请求的类别为OpCode.check
	最后操作记录为CHEC
	动态构造rsp = new SetDataResponse(rc.stat);
如果请求的类别为OpCode.exists
	最后操作记录为EXIS
	动态构造new ExistsRequest()
	反向序列化
	Stat stat = zks.getZKDatabase().statNode(path, existsRequest.getWatch() ? cnxn : null);
    rsp = new ExistsResponse(stat);
    requestPathMetricsCollector.registerRequest(request.type, path);
 如果请求的类别为OpCode.getData
 	最后操作记录为GETD
 	动态构造new GetDataRequest()
 	反向序列化
 	path = getDataRequest.getPath();
    rsp = handleGetDataRequest(getDataRequest, cnxn, request.authInfo);
    requestPathMetricsCollector.registerRequest(request.type, path);
 如果请求的类别为OpCode.setWatches
 	最后操作记录为SETW
 	动态构造new SetWatches()
 	反向序列化
 	long relativeZxid = setWatches.getRelativeZxid();
    zks.getZKDatabase().setWatches(relativeZxid, setWatches.getDataWatches(), setWatches.getExistWatches(), setWatches.getChildWatches(), Collections.emptyList(), Collections.emptyList(), cnxn);
    如果请求的类别为OpCode.setWatches2
    	最后操作记录为STW2
    	动态构造new SetWatches2()
    	反向序列化
    	long relativeZxid = setWatches.getRelativeZxid();
        zks.getZKDatabase().setWatches(relativeZxid, setWatches.getDataWatches(), setWatches.getExistWatches(), setWatches.getChildWatches(), setWatches.getPersistentWatches(), setWatches.getPersistentRecursiveWatches(), cnxn);
   如果请求的类别为OpCode.addWatch
   		最后操作记录为ADDW
   		动态构造new AddWatchRequest()
   		反向序列化
   		zks.getZKDatabase().addWatch(addWatcherRequest.getPath(), cnxn, addWatcherRequest.getMode());
        rsp = new ErrorResponse(0);
  如果请求的类别为OpCode.getACL
  		略
  如果请求的类别为OpCode.getChildren
  		最后操作记录为GETC
  		动态构造new GetChildrenRequest()
  		反向序列化
  		path = getChildrenRequest.getPath();
        rsp = handleGetChildrenRequest(getChildrenRequest, cnxn, request.authInfo);
        requestPathMetricsCollector.registerRequest(request.type, path);
  如果请求的类别为OpCode.getAllChildrenNumber
  		最后操作记录为GETACN
  		动态构造new GetAllChildrenNumberRequest();
  		反向序列化
  		path = getAllChildrenNumberRequest.getPath();
        DataNode n = zks.getZKDatabase().getNode(path);
        if (n == null) 
        {
               throw new KeeperException.NoNodeException();
        }
                
        zks.checkACL(request.cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ, request.authInfo, path, null);
        int number = zks.getZKDatabase().getAllChildrenNumber(path);
        rsp = new GetAllChildrenNumberResponse(number)
  如果请求的类别为OpCode.getChildren2
  		最后操作记录为GETC
  		动态构造new GetChildren2Request()
  		反向序列化
  		Stat stat = new Stat();
        path = getChildren2Request.getPath();
        DataNode n = zks.getZKDatabase().getNode(path);
        if (n == null) 
        {
              throw new KeeperException.NoNodeException();
        }
                
        zks.checkACL(request.cnxn, zks.getZKDatabase().aclForNode(n), ZooDefs.Perms.READ, request.authInfo, path, null);
        List<String> children = zks.getZKDatabase().getChildren(path, stat, getChildren2Request.getWatch() ? cnxn : null);
        rsp = new GetChildren2Response(children, stat);
        requestPathMetricsCollector.registerRequest(request.type, path);
如果请求的类别为OpCode.removeWatches
		最后操作记录为REMW
		动态构造new RemoveWatchesRequest()
		反向序列化
		WatcherType type = WatcherType.fromInt(removeWatches.getType());
        path = removeWatches.getPath();
        boolean removed = zks.getZKDatabase().removeWatch(path, type, cnxn);
        if (!removed) 
        {
            String msg = String.format(Locale.ENGLISH, "%s (type: %s)", path, type);
            throw new KeeperException.NoWatcherException(msg);
        }    
        requestPathMetricsCollector.registerRequest(request.type, removeWatches.getPath());
如果请求的类别为OpCode.whoAmI
		最后操作记录为HOMI
		rsp = new WhoAmIResponse(AuthUtil.getClientInfos(request.authInfo));
如果请求的类别为OpCode.getEphemerals
		最后操作记录为GETE
		GetEphemeralsRequest getEphemerals = new GetEphemeralsRequest();
        ByteBufferInputStream.byteBuffer2Record(request.request, getEphemerals);
        String prefixPath = getEphemerals.getPrefixPath();
        Set<String> allEphems = zks.getZKDatabase().getDataTree().getEphemerals(request.sessionId);
        List<String> ephemerals = new ArrayList<>();
        if (prefixPath == null || prefixPath.trim().isEmpty() || "/".equals(prefixPath.trim())) 
        {
            ephemerals.addAll(allEphems);
        } 
        else 
        {
             for (String p : allEphems) 
             {
                  if (p.startsWith(prefixPath)) 
                  {
                      ephemerals.add(p);
                  }
              }
         }
        
        rsp = new GetEphemeralsResponse(ephemerals);

	ReplyHeader hdr = new ReplyHeader(request.cxid, lastZxid, err.intValue());
    updateStats(request, lastOp, lastZxid);
    try 
    {
        if (path == null || rsp == null) 
        {
            responseSize = cnxn.sendResponse(hdr, rsp, "response");
        } 
        else 
        {
            int opCode = request.type;
            Stat stat = null;
            switch (opCode) 
            {
                case OpCode.getData : 
                {
                    GetDataResponse getDataResponse = (GetDataResponse) rsp;
                    stat = getDataResponse.getStat();
                    responseSize = cnxn.sendResponse(hdr, rsp, "response", path, stat, opCode);
                    break;
                }
                case OpCode.getChildren2 : 
                {
                    GetChildren2Response getChildren2Response = (GetChildren2Response) rsp;
                    stat = getChildren2Response.getStat();
                    responseSize = cnxn.sendResponse(hdr, rsp, "response", path, stat, opCode);
                    break;
                }
                default:
                    responseSize = cnxn.sendResponse(hdr, rsp, "response");
            }
        }

        if (request.type == OpCode.closeSession) 
        {
            cnxn.sendCloseSession();
        }
    } 
    catch (IOException e) 
    {
        LOG.error("FIXMSG", e);
    } 
    finally 
    {
        ServerMetrics.getMetrics().RESPONSE_BYTES.add(responseSize);
    }
applyRequest
ProcessTxnResult rc = zks.processTxn(request);
if (request.type == OpCode.closeSession && connClosedByClient(request)) 
{
        if (closeSession(zks.serverCnxnFactory, request.sessionId) || closeSession(zks.secureServerCnxnFactory, request.sessionId)) 
        {
            return rc;
        }
}

SyncRequestProcessor–执行体

将请求的事务头,事务体,事务摘要写入事务日志文件
满足生成快照条件时
	事务日志刷新到磁盘
	生成快照文件
toFlush.add(si)
满足刷新条件时,
	刷新事务日志文件内容到磁盘
	清空toFlush
如果请求不需写事务日志且没有等待刷新请求
	if (nextProcessor != null) 
    {
        nextProcessor.processRequest(si);
        if (nextProcessor instanceof Flushable) 
        {
            ((Flushable) nextProcessor).flush();
        }
    }
刷新
zks.getZKDatabase().commit();
    if (this.nextProcessor == null) 
    {
        this.toFlush.clear();
    } 
    else 
    {
        while (!this.toFlush.isEmpty()) 
        {
            final Request i = this.toFlush.remove();
            this.nextProcessor.processRequest(i);
        }
        
        if (this.nextProcessor instanceof Flushable) 
        {
            ((Flushable) this.nextProcessor).flush();
        }
    }
processRequest
queuedRequests.add(request);

SendAckRequestProcessor

processRequest
如果包类型不是OpCode.sync
	QuorumPacket qp = new QuorumPacket(Leader.ACK, si.getHdr().getZxid(), null, null);
	learner.writePacket(qp, false);

充当集群观察者的主节点–暂不分析观察者