五、请求处理流程

5.1 处理器链的组装

上节我们知道主从数据同步之后,zookeeper服务才正式启动,启动的第一步就是处理器链的组装,每一个客户端的请求都会经过这个处理器链上的处理器。下图是leader节点组装后的处理器链
image.png

源码如下:

@Override
protected void setupRequestProcessors() {
    // 最终处理器
    RequestProcessor finalProcessor = new FinalRequestProcessor(this);
    RequestProcessor toBeAppliedProcessor = new Leader.ToBeAppliedRequestProcessor(
            finalProcessor, getLeader().toBeApplied);
    // 提交处理器
    commitProcessor = new CommitProcessor(toBeAppliedProcessor,
            Long.toString(getServerId()), false);
    commitProcessor.start();
    // 事务处理器(内部包含了同步处理器SyncRequestProcessor、ACK处理器AckRequestProcessor)
    ProposalRequestProcessor proposalProcessor = new ProposalRequestProcessor(this,
            commitProcessor);
    proposalProcessor.initialize();
    // 请求预处理器
    firstProcessor = new PrepRequestProcessor(this, proposalProcessor);
    ((PrepRequestProcessor)firstProcessor).start();
}

5.2 请求的具体处理流程

处理请求的流程图如下:
image.png

  1. PrepRequestProcessor(请求预处理器)将请求Request封装成更新记录ChangeRecord,并置入outstandingChanges(进行中的更新记录),并将请求转发给事务处理器
  2. ProposalRequestProcessor(事务处理器):内部包含了同步处理器SyncRequestProcessor、ACK处理器AckRequestProcessor。将请求Request封装成Proposal事务,发送给其他follower节点,并转发给同步处理器。

    SyncRequestProcessor(同步处理器)将请求Request写入本地日志文件中,并转发给ACK处理器
    AckRequestProcessor(ACK处理器):当超过半数节点返回该事务的ACK,提交该事务给提交处理器CommitProcessor

  3. CommitProcessor(提交处理器):将请求Request转发给最终处理器FinalRequestProcessor
  4. FinalRequestProcessor(最终处理器):将请求Request更新到内存数据中,返回响应

5.2.1 请求预处理器PrepRequestProcessor

它将请求Request封装成更新记录ChangeRecord,并置入outstandingChanges,部分源码如下

org.apache.zookeeper.server.PrepRequestProcessor

protected void pRequest(Request request) throws RequestProcessorException {
// 判断请求类型
switch (request.type) {
    case OpCode.create:
        CreateRequest createRequest = new CreateRequest();
        // 准备请求的两阶段事务(将请求封装成ChangeRecord)
        pRequest2Txn(request.type, zks.getNextZxid(), request, createRequest, true);
        break;
    case OpCode.delete:
        DeleteRequest deleteRequest = new DeleteRequest();               
        pRequest2Txn(request.type, zks.getNextZxid(), request, deleteRequest, true);
        break;
    case OpCode.setData:
        SetDataRequest setDataRequest = new SetDataRequest();                
        pRequest2Txn(request.type, zks.getNextZxid(), request, setDataRequest, true);
        break;
    case OpCode.setACL:
        SetACLRequest setAclRequest = new SetACLRequest();                
        pRequest2Txn(request.type, zks.getNextZxid(), request, setAclRequest, true);
        break;
    case OpCode.check:
        CheckVersionRequest checkRequest = new CheckVersionRequest();         
        pRequest2Txn(request.type, zks.getNextZxid(), request, checkRequest, true);
        break;
    case OpCode.multi:
    case OpCode.createSession:
    case OpCode.closeSession:
        pRequest2Txn(request.type, zks.getNextZxid(), request, null, true);
        break;
    case OpCode.sync:
    case OpCode.exists:
    case OpCode.getData:
    case OpCode.getACL:
    case OpCode.getChildren:
    case OpCode.getChildren2:
    case OpCode.ping:
    case OpCode.setWatches:
        zks.sessionTracker.checkSession(request.sessionId,
                request.getOwner());
        break;
    }

    request.zxid = zks.getZxid();
    // 转发请求给下一个处理器
    nextProcessor.processRequest(request);
}

5.2.2 事务处理器ProposalRequestProcessor(包含SyncRequestProcessor、AckRequestProcessor)

事务处理器接收到请求后,会先将请求发送给提交处理器CommitProcessor,但此时提交处理器CommitProcessor还不会立即处理该请求,提交处理器需要等到ACK处理器真正提交该请求后,才会处理该请求。将请求封装成事务,发送给其他follower节点,并同时将请求发送给同步处理器SyncRequestProcessor,源码如下

org.apache.zookeeper.server.ProposalRequestProcessor

public void processRequest(Request request) throws RequestProcessorException {
    
    if(request instanceof LearnerSyncRequest){
        zks.getLeader().processSync((LearnerSyncRequest)request);
    } else {
        // 先将请求发送给提交处理器CommitProcessor,提交处理器会等待该请求的真正提交
        nextProcessor.processRequest(request);
        if (request.hdr != null) {
            try {
                // 将请求封装成事务,发送给其他follower节点
                zks.getLeader().propose(request);
            } catch (XidRolloverException e) {
                throw new RequestProcessorException(e.getMessage(), e);
            }
            // 将请求发送给同步处理器SyncRequestProcessor
            syncProcessor.processRequest(request);
        }
    }
}

同步处理器SyncRequestProcessor会将请求写入到日志文件中,并且记录请求数,如果请求数到达某个值,会生成快照文件(前面章节有介绍快照文件的生成策略)

@Override
public void run() {
  while (true) {
      Request si = null;
      if (toFlush.isEmpty()) {
          si = queuedRequests.take();
      } else {
          si = queuedRequests.poll();
          if (si == null) {
              flush(toFlush);
              continue;
          }
      }
      if (si == requestOfDeath) {
          break;
      }
      if (si != null) {
          // 将请求Request写入缓存中
          if (zks.getZKDatabase().append(si)) {
              logCount++;
              if (logCount > (snapCount / 2 + randRoll)) {
                  // 生成快照文件
              }
          } else if (toFlush.isEmpty()) {
              if (nextProcessor != null) {
                  // 将请求转发给ACK处理器
                  nextProcessor.processRequest(si);
              }
              continue;
          }
          toFlush.add(si);
          // 当缓存的请求数大于1000时,写入到磁盘的日志文件
          if (toFlush.size() > 1000) {
              flush(toFlush);
          }
      }
  }
}

private void flush(LinkedList<Request> toFlush) {
    if (toFlush.isEmpty())
        return;

    // 将缓存写入到磁盘的日志文件中
    zks.getZKDatabase().commit();
    while (!toFlush.isEmpty()) {
        Request i = toFlush.remove();
        if (nextProcessor != null) {
            // 将请求转发给ACK处理器
            nextProcessor.processRequest(i);
        }
    }
}

ACK处理器AckRequestProcessor会对接收到的事务进行确认(leader节点),此时还需要等待follower节点对该事务的确认,当超过半数节点确认该事务,leader节点才会真正提交该事务对应的请求Request给提交处理器CommitProcessor。源码如下

synchronized public void processAck(long sid, long zxid, SocketAddress followerAddr) {
    if (outstandingProposals.size() == 0) {
        return;
    }
    if (lastCommitted >= zxid) {
        return;
    }
    Proposal p = outstandingProposals.get(zxid);
    // 添加确认
    p.ackSet.add(sid);

    // 如果超过半数节点确认
    if (self.getQuorumVerifier().containsQuorum(p.ackSet)){             
        outstandingProposals.remove(zxid);
        // 给其他follower节点发送COMMIT命令
        commit(zxid);
        // 真正提交请求Request给提交处理器CommitProcessor
        zk.commitProcessor.commit(p.request);
    }
}

5.2.3 提交处理器CommitProcessor

提交处理器接收到真正提交的请求Request之后,会将请求Request转发给最终处理器FinalRequestProcessor

5.2.4 最终处理器FinalRequestProcessor

最终处理器接收到请求Request之后,会将请求Request的数据更新到内存中,并返回响应信息,源码如下:

synchronized (zks.outstandingChanges) {
    if (request.hdr != null) {
       TxnHeader hdr = request.hdr;
       Record txn = request.txn;
       // 将请求数据更新到内存中
       rc = zks.processTxn(hdr, txn);
    }
    
    if (Request.isQuorum(request.type)) {
        // 添加到近期事务列表中
        zks.getZKDatabase().addCommittedProposal(request);
    }
}

long lastZxid = zks.getZKDatabase().getDataTreeLastProcessedZxid();
ReplyHeader hdr = new ReplyHeader(request.cxid, lastZxid, err.intValue());
try {
    // 返回响应信息
    cnxn.sendResponse(hdr, rsp, "response");
    if (closeSession) {
        cnxn.sendCloseSession();
    }
} catch (IOException e) {
    LOG.error("FIXMSG",e);
}

至此请求的大致处理流程就结束了


kamier
1.5k 声望493 粉丝