在LearnerHandler.run()和Follower.followLeader()方法中存在顺序性的交互操作.那么Leader.lead()只是根据上述2个方法的交互作为状态判断(有同步的过程). 这是一个可读性比较差的实现. 状态完全依赖于顺序性交互顺序. 先画时序交互+流程逻辑图.
1.再次验证选举的ack waitForEpochAck先跑下单测 LearnerTest.syncTestZab1_0Test.testNormalObserverRun() 这个也只是启动和同步过程. 涉及到了infom proposal commit ,
LinkedList<Long> packetsCommitted = new LinkedList<Long>();对照代码进行注释. 后续加上. protected void syncWithLeader(long newLeaderZxid) throws IOException, InterruptedException{
LinkedList<PacketInFlight> packetsNotCommitted = new LinkedList<PacketInFlight>();
}
这两个临时变量承载了flower未启动是的proposal和commit指令传送过来的数据.
如果是FollowerZooKeeperServer最终会转存到FollowerZooKeeperServer.pendingTxns,
同时需要syncProcessor.processRequest(request);不同的角色落盘后的操作是不同的.
SyncRequestProcessor的作用. sync数据到磁盘,并且发送ack消息给Leader(通过后续的AckRequestProcessor操作,Observer不发送,注释中有说明).解析:Zab1_0Test.testNormalObserverRun()
1.该单测启动了一个Observer
2.模拟了一个LEADER,手动操作Leader的ia和oa发送,接受数据.ObserverConversation 取名为观察者交互,注意就是为了测试Observer用的. 主要是 提供了Leader的ia和oa,操作这两个和观察者交互.
仅仅在同步方法的死循环中接受命令和操作. 并没有在后续的正常工作后的监听后接受到任何数据.
在数据同步结束前Leader.UPTODATE,还发送了一个proposal,commit和infom指令.这些都是再observer的同步代码中接受.
之后就没有再发送过消息了.@Test public void testNormalObserverRun() throws Exception { testObserverConversation(new ObserverConversation() { @Override public void converseWithObserver(InputArchive ia, OutputArchive oa, Observer o) throws Exception { File tmpDir = File.createTempFile("test", "dir", testData); tmpDir.delete(); tmpDir.mkdir(); File logDir = o.zk.getTxnLogFactory().getDataDir().getParentFile(); File snapDir = o.zk.getTxnLogFactory().getSnapDir().getParentFile(); try { Assert.assertEquals(0, o.self.getAcceptedEpoch()); Assert.assertEquals(0, o.self.getCurrentEpoch()); // Setup a database with a single /foo node ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir)); final long foo1Zxid = ZxidUtils.makeZxid(1, 1); final long foo2Zxid = ZxidUtils.makeZxid(1, 2); zkDb.processTxn(new TxnHeader(13, 1313, foo1Zxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo1", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1)); zkDb.processTxn(new TxnHeader(13, 1313, foo2Zxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo2", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1)); Stat stat = new Stat(); Assert.assertEquals("data1", new String(zkDb.getData("/foo1", stat, null))); Assert.assertEquals("data1", new String(zkDb.getData("/foo2", stat, null))); QuorumPacket qp = new QuorumPacket(); readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.OBSERVERINFO, qp.getType()); Assert.assertEquals(qp.getZxid(), 0); LearnerInfo learnInfo = new LearnerInfo(); ByteBufferInputStream.byteBuffer2Record( ByteBuffer.wrap(qp.getData()), learnInfo); Assert.assertEquals(learnInfo.getProtocolVersion(), 0x10000); Assert.assertEquals(learnInfo.getServerid(), 0); // We are simulating an established leader, so the epoch is 1 qp.setType(Leader.LEADERINFO); qp.setZxid(ZxidUtils.makeZxid(1, 0)); byte protoBytes[] = new byte[4]; ByteBuffer.wrap(protoBytes).putInt(0x10000); qp.setData(protoBytes); oa.writeRecord(qp, null); readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.ACKEPOCH, qp.getType()); Assert.assertEquals(0, qp.getZxid()); Assert.assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer .wrap(qp.getData()).getInt()); Assert.assertEquals(1, o.self.getAcceptedEpoch()); Assert.assertEquals(0, o.self.getCurrentEpoch()); // Send the snapshot we created earlier qp.setType(Leader.SNAP); qp.setData(new byte[0]); qp.setZxid(zkDb.getDataTreeLastProcessedZxid()); oa.writeRecord(qp, null); zkDb.serializeSnapshot(oa); oa.writeString("BenWasHere", null); qp.setType(Leader.NEWLEADER); qp.setZxid(ZxidUtils.makeZxid(1, 0)); oa.writeRecord(qp, null); // Get the ack of the new leader readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.ACK, qp.getType()); Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); Assert.assertEquals(1, o.self.getAcceptedEpoch()); Assert.assertEquals(1, o.self.getCurrentEpoch()); Assert.assertEquals(foo2Zxid, o.zk.getLastProcessedZxid()); // Make sure the data was recorded in the filesystem ok ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog( logDir, snapDir)); long lastZxid = zkDb2.loadDataBase(); Assert.assertEquals("data1", new String(zkDb2.getData("/foo1", stat, null))); Assert.assertEquals(foo2Zxid, lastZxid); // Register watch TrackerWatcher watcher = new TrackerWatcher(); Assert.assertEquals("data1", new String(o.zk .getZKDatabase().getData("/foo2", stat, watcher))); // Propose /foo1 update long proposalZxid = ZxidUtils.makeZxid(1, 1000); proposeSetData(qp, "/foo1", proposalZxid, "data2", 2); oa.writeRecord(qp, null); // Commit /foo1 update qp.setType(Leader.COMMIT); qp.setZxid(proposalZxid); oa.writeRecord(qp, null); // Inform /foo2 update long informZxid = ZxidUtils.makeZxid(1, 1001); proposeSetData(qp, "/foo2", informZxid, "data2", 2); qp.setType(Leader.INFORM); oa.writeRecord(qp, null); qp.setType(Leader.UPTODATE); qp.setZxid(0); oa.writeRecord(qp, null); // Read the uptodate ack readPacketSkippingPing(ia, qp); Assert.assertEquals(Leader.ACK, qp.getType()); Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid()); // Data should get updated watcher.waitForChange(); Assert.assertEquals("data2", new String(o.zk .getZKDatabase().getData("/foo1", stat, null))); Assert.assertEquals("data2", new String(o.zk .getZKDatabase().getData("/foo2", stat, null))); // Shutdown sequence guarantee that all pending requests // in sync request processor get flush to disk o.zk.shutdown(); zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir)); lastZxid = zkDb2.loadDataBase(); Assert.assertEquals("data2", new String(zkDb2.getData("/foo1", stat, null))); Assert.assertEquals("data2", new String(zkDb2.getData("/foo2", stat, null))); Assert.assertEquals(informZxid, lastZxid); } finally { recursiveDelete(tmpDir); } } private void proposeSetData(QuorumPacket qp, String path, long zxid, String data, int version) throws IOException { qp.setType(Leader.PROPOSAL); qp.setZxid(zxid); TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.setData); SetDataTxn sdt = new SetDataTxn(path, data.getBytes(), version); ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputArchive boa = BinaryOutputArchive.getArchive(baos); boa.writeRecord(hdr, null); boa.writeRecord(sdt, null); qp.setData(baos.toByteArray()); } }); }
Leader:
2.再次验证newLeaderAck waitForNewLeaderAck 边界类是LearnerCnxAcceptor对应的连接LearnerHandlerObserver:1.接受leader会发过来的 Leader.UPTODATE 指令 break outerLoop;2. 处理相关Leader.proposal和Leader.commit.两种的TXN./** * This message type is sent by a leader to propose a mutation. */ public final static int Leader.PROPOSAL = 2; 进入packetsNotCommitted/** * This message type is sent by a leader to commit a proposal and cause * followers to start serving the corresponding data. */ final static int Leader.COMMIT = 4; 进入packetsCommittedFollowerZooKeeperServer fzk = (FollowerZooKeeperServer)zk; for(PacketInFlight p: packetsNotCommitted) { fzk.logRequest(p.hdr, p.rec); } for(Long zxid: packetsCommitted) { fzk.commit(zxid); } 魔法数字: zookeeper 0x10000 代表版本号 Learner类 LearnerInfo li = new LearnerInfo(self.getId(), 0x10000); Leader里的LearnerHandler类:
附录:
zookeeper详细 时序+逻辑流程图.传上来就颠倒了,尴尬.
选举完毕之后的数据同步.
最新推荐文章于 2024-03-19 01:00:00 发布