What is the difference between zookeeper closing the connection using the close command and closing the running window directly for temporary nodes

  Through the source code, we can know that after the zookeeper client is connected to the server, the server will generate a session for it and negotiate the timeout of the session with the client. The automatic deletion is done through the sessionTracker thread [SessionTrackerImpl implementation class]. The thread will monitor the session expiration queue in real time. If any session expires, it will be added to this queue, and the isClosing state of the corresponding expired session will be set to true, and then the close method in the expire method will be used to send a closeSession request to the service The end itself closes the session. As shown in the figure and code below.
Insert picture description here

public void expire(Session session) {
    
    
    long sessionId = session.getSessionId();
    LOG.info(
        "Expiring session 0x{}, timeout of {}ms exceeded",
        Long.toHexString(sessionId),
        session.getTimeout());

    close(sessionId); //
}
private void close(long sessionId) {
    
    
// 清空关于session的map,删除临时节点 prepReqeustProcessor sync final
    Request si = new Request(null, sessionId, 0, OpCode.closeSession, null, null);
    submitRequest(si);
}

  When we run a client through the command line window, the corresponding server will generate a session bound to it. If we close the client connection by closing the window, the temporary node on the server will not be deleted immediately , But wait until the session expires before all the temporary nodes on the session are deleted. Even if you close the window and re-use the command line window to run, it will not prevent the temporary node from being deleted, because the server will re-bind a different session to it when the window is reopened and run. [The essence is that a closeSession transaction request is submitted after the session expires]
  If we close the client connection through the close command, the client will send a closeSession transaction request to the server, and the server will log it and store it on the memory datatree All related temporary nodes are deleted, and then the connection is closed.

Next, let's take a look at the process of deleting session temporary nodes

  1. The closeSession transaction request will first go through the PrepRequestProcessor. Please see the figure below, and then hand it to the SyncRequestProcessor.
    Insert picture description here
    Let’s look at the pRequest2Txn method. I will intercept the related code.
protected void pRequest2Txn(int type, long zxid, Request request, Record record, boolean deserialize) throws KeeperException, IOException, RequestProcessorException {
    
    
        // 这是日志头
        if (request.getHdr() == null) {
    
    
                // sessionID、cxid、zxid、当前时间、请求类型
                request.setHdr(new TxnHeader(request.sessionId, request.cxid, zxid,
                     Time.currentWallTime(), type));
        }
switch (type) {
    
    
            case OpCode.closeSession:
    // We don't want to do this check since the session expiration thread
    // queues up this operation without being the session owner.
    // this request is the last of the session so it should be ok
    //zks.sessionTracker.checkSession(request.sessionId, request.getOwner());
    long startTime = Time.currentElapsedTime();
    synchronized (zks.outstandingChanges) {
    
    
            Set<String> es = zks.getZKDatabase().getEphemerals(request.sessionId);
            for (ChangeRecord c : zks.outstandingChanges) {
    
    
                if (c.stat == null) {
    
    
                    // Doing a delete
                    es.remove(c.path);
                } else if (c.stat.getEphemeralOwner() == request.sessionId) {
    
    
                    es.add(c.path);
                }
            }
            for (String path2Delete : es) {
    
    
                if (digestEnabled) {
    
    
                    parentPath = getParentPathAndValidate(path2Delete);
                    parentRecord = getRecordForPath(parentPath);
                    parentRecord = parentRecord.duplicate(request.getHdr().getZxid());
                    parentRecord.stat.setPzxid(request.getHdr().getZxid());
                    parentRecord.precalculatedDigest = precalculateDigest(
                        DigestOpCode.UPDATE, parentPath, parentRecord.data, parentRecord.stat);
                    addChangeRecord(parentRecord);
                }
           标红   nodeRecord = new ChangeRecord(
                    request.getHdr().getZxid(), path2Delete, null, 0, null);
                nodeRecord.precalculatedDigest = precalculateDigest(
                    DigestOpCode.REMOVE, path2Delete);
            标红    addChangeRecord(nodeRecord);
            }
            if (ZooKeeperServer.isCloseSessionTxnEnabled()) {
    
    
            // 持久化
                request.setTxn(new CloseSessionTxn(new ArrayList<String>(es)));
            }
            标红 zks.sessionTracker.setSessionClosing(request.sessionId);
        }
        ServerMetrics.getMetrics().CLOSE_SESSION_PREP_TIME.add(Time.currentElapsedTime() - startTime);
        break;
}

  Look at the code marked in red, it will add a ChangeRecord with null stat attribute to all temporary nodes related to the session and add it to the outstandingChanges queue and outstandingChangesForPath queue, and then call the setSessionClosing method of the sessionTracker thread to change the state of the session isClosing Set to true.

Here we need to popularize why we need to use ChangeRecord to understand the above code better

ChangeRecord represents the modification record, which represents the modification record of a certain node. When processing the Request, it needs to rely on the existing information on the existing node, such as cversion (the version of the child node of a certain node). For example, when processing a create request, Need to modify the cversion (plus 1) on the parent node, so where does this information come from? It must be from DataTree at the beginning, but it is not possible to get the information of the parent node from DataTree every time, so the performance is very slow. For example, ZooKeeperServer receives two create requests in a row. When a create request is processed, it needs to be processed first. Obtaining information from DataTree, then persisting, then updating DataTree, and finally the next create request can be processed. It is a serial process, so what if the second create is illegal? According to the above idea, you still need to wait for the first create request to be processed before the second request can be verified. Therefore, in order to solve this problem, Zookeeper, in PrepRequestProcessor, before verifying a request, the request is asynchronously submitted. For the persistence thread to process, PrepRequestProcessor processes the next request by itself, interrupting the serial link, but then there is a problem again, because it needs to rely on the information of the parent node when processing the second create request. And it should have processed the result of the first create request, so ChangeRecord is introduced at this time. When PrepRequestProcessor processes the first create request, it first generates a ChangeRecord record, and then asynchronously de-persistes and updates the DataTree, and then immediately To process the second create request, you do not need to fetch the information in the DataTree at this time (even if you do, you may get the wrong information), just fetch the information in the ChangeRecord directly. [Simple understanding is that because there is a queue, it needs to be asynchronous, and because it is asynchronous, ChangeRecord is needed].

  1. The closeSession transaction request will then be logged and persisted through the SyncRequestProcessor , and then handed over to the FinalRequestProcessor
  2. eventually by a transaction request closeSession FinalRequestProcessor processed
      start FinalRequestProcessor the processRequest method 124 rows ProcessTxnResult zks.processTxn RC = (Request) ; Start
      then ZooKeeperServer 1812 rows processTxn method of ProcessTxnResult rc = processTxnInDB (hdr, request.getTxn () , request.getTxnDigest ());
      then to the 1872 line ZooKeeperServer processTxnInDB method of return getZKDatabase () processTxn (hdr,
    txn, digest);.   then ZKDatabase 489 rows processTxn method of dataTree.processTxn (hdr, txn, digest) ;
      then DataTree 895 line processTxn methodThe ProcessTxnResult result = processTxn(header, txn);
      Then line 904 of the ProcessTxn method of DataTree, line 1001 of the processTxn method , look at the following figure
    Insert picture description here
    , click again, line 1184 of DataTree, look at the figure below

Insert picture description here
  In this way, the temporary nodes of the memory DataTree are all deleted. At this point, the closeSession record will be recorded in the log record, but the related temporary nodes on the memory DataTree have all been deleted, so this is why when the client uses the close command to close the connection, it cannot be seen even if it reconnects soon. The reason for the temporary node of the previous session.

Guess you like

Origin blog.csdn.net/gwokgwok137/article/details/113922295