公司内部使用zk的watcher功能做一个分布式监听器,用来做一个数据变更通知,但是出现了数据不同步的问题,查看了zk相关源码,发现zk的watcher在触发一次后,就会从监视队列中移除,也就是说,watcher是一次性的。
之前为了解决数据变更通知不丢消息的问题,考虑自己来实现一个简单的watcher机制,然后就考虑去扒一下zk的源码,稍微改一改,弄个简易版的watcher满足我们的需求即可,于是去github上clone了最新的代码,找到watcher触发相关的部分。Emmmm?!?! 我们一起来看一哈。
PS:在看Curator相关代码的时候,发现了关联的zk的任务–zk增加了持久化递归监视
https://issues.apache.org/jira/browse/ZOOKEEPER-1416
首先我切到3.6版本的分支,因为我在服务器上部署的是3.6.2版本的zk,所以branch_3.6应该是对应的我部署的server版本的代码。接收请求的流程就不详细说了,网上很多解析(关键是具体我也没看,直接百度然后找到重点代码来看的/狗头),请求最终都会走到这个类里面org.apache.zookeeper.server.FinalRequestProcessor
,然后执行这个方法org.apache.zookeeper.server.FinalRequestProcessor#processRequest
。
public void processRequest(Request request) {
LOG.debug("Processing request:: {}", request);
// request.addRQRec(">final");
long traceMask = ZooTrace.CLIENT_REQUEST_TRACE_MASK;
if (request.type == OpCode.ping) {
traceMask = ZooTrace.SERVER_PING_TRACE_MASK;
}
if (LOG.isTraceEnabled()) {
ZooTrace.logRequest(LOG, traceMask, 'E', request, "");
}
ProcessTxnResult rc = zks.processTxn(request);
// 以下省略成吨的代码
}
我们重点看一下zks.processTxn(request);
这个方法。点进去
public ProcessTxnResult processTxn(Request request) {
TxnHeader hdr = request.getHdr();
processTxnForSessionEvents(request, hdr, request.getTxn());
final boolean writeRequest = (hdr != null);
final boolean quorumRequest = request.isQuorum();
// return fast w/o synchronization when we get a read
if (!writeRequest && !quorumRequest) {
return new ProcessTxnResult();
}
synchronized (outstandingChanges) {
ProcessTxnResult rc = processTxnInDB(hdr, request.getTxn(), request.getTxnDigest());
// request.hdr is set for write requests, which are the only ones
// that add to outstandingChanges.
if (writeRequest) {
long zxid = hdr.getZxid();
while (!outstandingChanges.isEmpty()
&& outstandingChanges.peek().zxid <= zxid) {
ChangeRecord cr = outstandingChanges.remove();
ServerMetrics.getMetrics().OUTSTANDING_CHANGES_REMOVED.add(1);
if (cr.zxid < zxid) {
LOG.warn(
"Zxid outstanding 0x{} is less than current 0x{}",
Long.toHexString(cr.zxid),
Long.toHexString(zxid));
}
if (outstandingChangesForPath.get(cr.path) == cr) {
outstandingChangesForPath.remove(cr.path);
}
}
}
// do not add non quorum packets to the queue.
if (quorumRequest) {
getZKDatabase().addCommittedProposal(request);
}
return rc;
}
}
其中同步块中,ProcessTxnResult rc = processTxnInDB(hdr, request.getTxn(), request.getTxnDigest());
返回的类叫XXXResult,而方法名是processXXXInDB,而且入参是吧请求reqeust的一些东西丢了进去,应该是处理请求并得到结果,点进去可以看到都是参数传递,直接调用了下一层的方法,直到org.apache.zookeeper.server.DataTree#processTxn(TxnHeader, org.apache.jute.Record, boolean)
public ProcessTxnResult processTxn(TxnHeader header, Record txn, boolean isSubTxn) {
ProcessTxnResult rc = new ProcessTxnResult();
try {
rc.clientId = header.getClientId();
rc.cxid = header.getCxid();
rc.zxid = header.getZxid();
rc.type = header.getType();
rc.err = 0;
rc.multiResult = null;
switch (header.getType()) {
case OpCode.create:
CreateTxn createTxn = (CreateTxn) txn;
rc.path = createTxn.getPath();
createNode(
createTxn.getPath(),
createTxn.getData(),
createTxn.getAcl(),