Leader 同步数据给 Follower,由 LearnerHandler.syncFollower() 方法完成。
Case 1: 设置了 zookeeper.forceSnapshotSync 参数(一般测试使用),则强制用 snapshot 进行同步
Case 2: lastProcessedZxid == peerLastZxid Leader发送空的 DIFF 包给 Follower
Case 3: peerLastZxid > maxCommittedLog && !isPeerNewEpochZxid Leader发送 TRUNC 包给 Follower
Case 4: minCommittedLog <= peerLastZxid <= maxCommittedLog 用 commitlog 同步
Case 5: peerLastZxid < minCommittedLog && txnLogSyncEnabled 用 事务日志和commitlog 同步
画了个大致流程,如下图:
有几个 ID 这里说明下
lastProcessedZxid —— 故名思意,就是最近一次处理的事务 zxid。该值最初由最新的 snapshot 文件名中取得,如下图所示。
若 事务 日志中保存了更大的值,则在系统重启回放(playback 见下面第二张图)事务日志时,会再次更新 lastProcessedZxid 同时 也会设置 minCommittedLog 和 maxCommitedLog.
关于 minCommittedLog 和 maxCommitedLog
上面说的 minCommittedLog 和 maxCommitedLog 定义于 ZKDatabase 具体如下:
protected long minCommittedLog, maxCommittedLog; protected LinkedList<Proposal> committedLog = new LinkedList<Proposal>(); 这里需要提下 committedLog 列表,这个 list 干啥的呢,看下面的源码注释,原来是为了 leader 和 follower 之间快速完成数据同步用的缓存。 /** * maintains a list of last <i>committedLog</i> * or so committed requests. This is used for * fast follower synchronization. * @param request committed request */ public void addCommittedProposal(Request request) { WriteLock wl = logLock.writeLock(); try { wl.lock(); if (committedLog.size() > commitLogCount) { committedLog.removeFirst(); // 1 minCommittedLog = committedLog.getFirst().packet.getZxid(); // 2 } if (committedLog.isEmpty()) { minCommittedLog = request.zxid; maxCommittedLog = request.zxid; } byte[] data = SerializeUtils.serializeRequest(request); QuorumPacket pp = new QuorumPacket(Leader.PROPOSAL, request.zxid, data, null); Proposal p = new Proposal(); p.packet = pp; p.request = request; committedLog.add(p); // 3 maxCommittedLog = p.packet.getZxid(); // 4 } finally { wl.unlock(); } } 这个缓存的大小由 commitLogCount 控制,默认值为 500. 当 committedLog 长度大于 500,就把列表头部第一个 commit log 删除(见 1 处),而 minCommittedLog 的值永远是 committedLog 列表中第一个元素(见 2 处)。maxCommittedLog 指向 committedLog 列表的最后一个元素(见 3,4 处)。此外,每当有新提交的议案,都会调用 addCommittedProposal 添加到 committedLog 列表中。