@Override
 public void okResponse(byte[] ok, BackendConnection conn) {
   if (this.cmdHandler.relaseConOnOK()) {
     session.releaseConnection(conn);
   } else {
     session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);
   }
   if (this.finished()) {
     cmdHandler.okResponse(session, ok);
     if (cmdHandler.isAutoClearSessionCons()) {
       session.clearResources(false);
     }
   }
 }
  public void executeBatchNodeCmd(SQLCtrlCommand cmdHandler) {
    this.cmdHandler = cmdHandler;
    final int initCount = session.getTargetCount();
    runningCount.set(initCount);
    nodeCount = initCount;
    failed.set(false);
    faileCount.set(0);
    // 执行
    int started = 0;
    for (RouteResultsetNode rrn : session.getTargetKeys()) {
      if (rrn == null) {
        LOGGER.error("null is contained in RoutResultsetNodes, source = " + session.getSource());
        continue;
      }
      final BackendConnection conn = session.getTarget(rrn);
      if (conn != null) {
        conn.setResponseHandler(this);
        cmdHandler.sendCommand(session, conn);
        ++started;
      }
    }

    if (started < nodeCount) {
      runningCount.set(started);
      LOGGER.warn("some connection failed to execut " + (nodeCount - started));
      /**
       * assumption: only caused by front-end connection close. <br>
       * Otherwise, packet must be returned to front-end
       */
      failed.set(true);
    }
  }
  @Override
  public void errorResponse(byte[] err, BackendConnection conn) {
    faileCount.incrementAndGet();

    if (this.cmdHandler.releaseConOnErr()) {
      session.releaseConnection(conn);
    } else {

      session.releaseConnectionIfSafe(conn, LOGGER.isDebugEnabled(), false);
    }
    if (this.finished()) {
      cmdHandler.errorResponse(session, err, this.nodeCount, this.faileCount.get());
      if (cmdHandler.isAutoClearSessionCons()) {
        session.clearResources(session.getSource().isTxInterrupted());
      }
    }
  }