tomcat系列(四) 连接器(下)

194 阅读6分钟

在上一章中,我们看到了processSocket()这个方法,他就是tomcat处理连接的代码入口了。通过线程的方式来启动,今天我们接着上一章的内容继续讲。

createSocketProcessor()

我们先看看这个方法

image.png 这是一个抽象方法,具体的实现交给子类来完成

image.png 我们nioEndPoint的实现里面就是new了一个对象

protected class SocketProcessor extends SocketProcessorBase<NioChannel> {

    public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
        super(socketWrapper, event);
    }

    @Override
    protected void doRun() {
        NioChannel socket = socketWrapper.getSocket();
        SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());

        try {
            int handshake = -1;

            try {
                if (key != null) {
                    if (socket.isHandshakeComplete()) {
                        // No TLS handshaking required. Let the handler
                        // process this socket / event combination.
                        handshake = 0;
                    } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                            event == SocketEvent.ERROR) {
                        // Unable to complete the TLS handshake. Treat it as
                        // if the handshake failed.
                        handshake = -1;
                    } else {
                        handshake = socket.handshake(key.isReadable(), key.isWritable());
                        // The handshake process reads/writes from/to the
                        // socket. status may therefore be OPEN_WRITE once
                        // the handshake completes. However, the handshake
                        // happens when the socket is opened so the status
                        // must always be OPEN_READ after it completes. It
                        // is OK to always set this as it is only used if
                        // the handshake completes.
                        event = SocketEvent.OPEN_READ;
                    }
                }
            } catch (IOException x) {
                handshake = -1;
                if (log.isDebugEnabled()) {
                    log.debug("Error during SSL handshake",x);
                }
            } catch (CancelledKeyException ckx) {
                handshake = -1;
            }
            if (handshake == 0) {
                SocketState state = SocketState.OPEN;
                // Process the request from this socket
                if (event == null) {
                    state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                } else {
                    state = getHandler().process(socketWrapper, event);
                }
                if (state == SocketState.CLOSED) {
                    close(socket, key);
                }
            } else if (handshake == -1 ) {
                getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);
                close(socket, key);
            } else if (handshake == SelectionKey.OP_READ){
                socketWrapper.registerReadInterest();
            } else if (handshake == SelectionKey.OP_WRITE){
                socketWrapper.registerWriteInterest();
            }
        } catch (CancelledKeyException cx) {
            socket.getPoller().cancelledKey(key);
        } catch (VirtualMachineError vme) {
            ExceptionUtils.handleThrowable(vme);
        } catch (Throwable t) {
            log.error("", t);
            socket.getPoller().cancelledKey(key);
        } finally {
            socketWrapper = null;
            event = null;
            //return to cache
            if (running && !paused) {
                processorCache.push(this);
            }
        }
    }
}

这个地方会发现,他好像没有run方法。然而这里又是使用了模板方法,dorun的具体调用时交给父类,也就是在上一章末尾看到的SocketProcessorBase()。

@Override
public final void run() {
    synchronized (socketWrapper) {
        // It is possible that processing may be triggered for read and
        // write at the same time. The sync above makes sure that processing
        // does not occur in parallel. The test below ensures that if the
        // first event to be processed results in the socket being closed,
        // the subsequent events are not processed.
        if (socketWrapper.isClosed()) {
            return;
        }
        doRun();
    }
}

回到doRun里面,里面的核心方法其实就是handler.process(),因为process方法代码很长,这里只截取中间核心调用的一块

SocketState state = SocketState.CLOSED;
do {
    state = processor.process(wrapper, status);

    if (state == SocketState.UPGRADING) {
        // Get the HTTP upgrade handler
        UpgradeToken upgradeToken = processor.getUpgradeToken();
        // Restore leftover input to the wrapper so the upgrade
        // processor can process it.
        ByteBuffer leftOverInput = processor.getLeftoverInput();
        wrapper.unRead(leftOverInput);
        if (upgradeToken == null) {
            // Assume direct HTTP/2 connection
            UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c");
            if (upgradeProtocol != null) {
                // Release the Http11 processor to be re-used
                release(processor);
                // Create the upgrade processor
                processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
                // Associate with the processor with the connection
                connections.put(socket, processor);
            } else {
                if (getLog().isDebugEnabled()) {
                    getLog().debug(sm.getString(
                        "abstractConnectionHandler.negotiatedProcessor.fail",
                        "h2c"));
                }
                // Exit loop and trigger appropriate clean-up
                state = SocketState.CLOSED;
            }
        } else {
            HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
            // Release the Http11 processor to be re-used
            release(processor);
            // Create the upgrade processor
            processor = getProtocol().createUpgradeProcessor(wrapper, upgradeToken);
            if (getLog().isDebugEnabled()) {
                getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
                        processor, wrapper));
            }
            // Associate with the processor with the connection
            connections.put(socket, processor);
            // Initialise the upgrade handler (which may trigger
            // some IO using the new protocol which is why the lines
            // above are necessary)
            // This cast should be safe. If it fails the error
            // handling for the surrounding try/catch will deal with
            // it.
            if (upgradeToken.getInstanceManager() == null) {
                httpUpgradeHandler.init((WebConnection) processor);
            } else {
                ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null);
                try {
                    httpUpgradeHandler.init((WebConnection) processor);
                } finally {
                    upgradeToken.getContextBind().unbind(false, oldCL);
                }
            }
        }
    }
} while ( state == SocketState.UPGRADING);

这里我们看他的关键方法processor.process

SocketState state = SocketState.CLOSED;
Iterator<DispatchType> dispatches = null;
do {
    if (dispatches != null) {
        DispatchType nextDispatch = dispatches.next();
        if (getLog().isDebugEnabled()) {
            getLog().debug("Processing dispatch type: [" + nextDispatch + "]");
        }
        state = dispatch(nextDispatch.getSocketStatus());
        if (!dispatches.hasNext()) {
            state = checkForPipelinedData(state, socketWrapper);
        }
    } else if (status == SocketEvent.DISCONNECT) {
        // Do nothing here, just wait for it to get recycled
    } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
        state = dispatch(status);
        state = checkForPipelinedData(state, socketWrapper);
    } else if (status == SocketEvent.OPEN_WRITE) {
        // Extra write event likely after async, ignore
        state = SocketState.LONG;
    } else if (status == SocketEvent.OPEN_READ) {
        state = service(socketWrapper);
    } else if (status == SocketEvent.CONNECT_FAIL) {
        logAccess(socketWrapper);
    } else {
        // Default to closing the socket if the SocketEvent passed in
        // is not consistent with the current state of the Processor
        state = SocketState.CLOSED;
    }

    if (getLog().isDebugEnabled()) {
        getLog().debug("Socket: [" + socketWrapper +
                "], Status in: [" + status +
                "], State out: [" + state + "]");
    }

    if (isAsync()) {
        state = asyncPostProcess();
        if (getLog().isDebugEnabled()) {
            getLog().debug("Socket: [" + socketWrapper +
                    "], State after async post processing: [" + state + "]");
        }
    }

    if (dispatches == null || !dispatches.hasNext()) {
        // Only returns non-null iterator if there are
        // dispatches to process.
        dispatches = getIteratorAndClearDispatches();
    }
} while (state == SocketState.ASYNC_END ||
        dispatches != null && state != SocketState.CLOSED);

return state;

这里if有很多的分支,但是源码里面都有注释。当我们请求进来的时候走的是OPEN_READ这个分支。然后我们就见到了熟悉的service方法

RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

// Setting up the I/O
setSocketWrapper(socketWrapper);

// Flags
keepAlive = true;
openSocket = false;
readComplete = true;
boolean keptAlive = false;
SendfileState sendfileState = SendfileState.DONE;

while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
        sendfileState == SendfileState.DONE && !endpoint.isPaused()) {

    // Parsing the request header
    try {
        if (!inputBuffer.parseRequestLine(keptAlive)) {
            if (inputBuffer.getParsingRequestLinePhase() == -1) {
                return SocketState.UPGRADING;
            } else if (handleIncompleteRequestLineRead()) {
                break;
            }
        }

        // Process the Protocol component of the request line
        // Need to know if this is an HTTP 0.9 request before trying to
        // parse headers.
        prepareRequestProtocol();

        if (endpoint.isPaused()) {
            // 503 - Service unavailable
            response.setStatus(503);
            setErrorState(ErrorState.CLOSE_CLEAN, null);
        } else {
            keptAlive = true;
            // Set this every time in case limit has been changed via JMX
            request.getMimeHeaders().setLimit(endpoint.getMaxHeaderCount());
            // Don't parse headers for HTTP/0.9
            if (!http09 && !inputBuffer.parseHeaders()) {
                // We've read part of the request, don't recycle it
                // instead associate it with the socket
                openSocket = true;
                readComplete = false;
                break;
            }
            if (!disableUploadTimeout) {
                socketWrapper.setReadTimeout(connectionUploadTimeout);
            }
        }
    } catch (IOException e) {
        if (log.isDebugEnabled()) {
            log.debug(sm.getString("http11processor.header.parse"), e);
        }
        setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
        break;
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        UserDataHelper.Mode logMode = userDataHelper.getNextMode();
        if (logMode != null) {
            String message = sm.getString("http11processor.header.parse");
            switch (logMode) {
                case INFO_THEN_DEBUG:
                    message += sm.getString("http11processor.fallToDebug");
                    //$FALL-THROUGH$
                case INFO:
                    log.info(message, t);
                    break;
                case DEBUG:
                    log.debug(message, t);
            }
        }
        // 400 - Bad Request
        response.setStatus(400);
        setErrorState(ErrorState.CLOSE_CLEAN, t);
    }

    // Has an upgrade been requested?
    if (isConnectionToken(request.getMimeHeaders(), "upgrade")) {
        // Check the protocol
        String requestedProtocol = request.getHeader("Upgrade");

        UpgradeProtocol upgradeProtocol = protocol.getUpgradeProtocol(requestedProtocol);
        if (upgradeProtocol != null) {
            if (upgradeProtocol.accept(request)) {
                // TODO Figure out how to handle request bodies at this
                // point.
                response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
                response.setHeader("Connection", "Upgrade");
                response.setHeader("Upgrade", requestedProtocol);
                action(ActionCode.CLOSE,  null);
                getAdapter().log(request, response, 0);

                InternalHttpUpgradeHandler upgradeHandler =
                        upgradeProtocol.getInternalUpgradeHandler(
                                getAdapter(), cloneRequest(request));
                UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null, requestedProtocol);
                action(ActionCode.UPGRADE, upgradeToken);
                return SocketState.UPGRADING;
            }
        }
    }

    if (getErrorState().isIoAllowed()) {
        // Setting up filters, and parse some request headers
        rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
        try {
            prepareRequest();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("http11processor.request.prepare"), t);
            }
            // 500 - Internal Server Error
            response.setStatus(500);
            setErrorState(ErrorState.CLOSE_CLEAN, t);
        }
    }

    if (maxKeepAliveRequests == 1) {
        keepAlive = false;
    } else if (maxKeepAliveRequests > 0 &&
            socketWrapper.decrementKeepAlive() <= 0) {
        keepAlive = false;
    }

    // Process the request in the adapter
    if (getErrorState().isIoAllowed()) {
        try {
            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
            getAdapter().service(request, response);
            // Handle when the response was committed before a serious
            // error occurred.  Throwing a ServletException should both
            // set the status to 500 and set the errorException.
            // If we fail here, then the response is likely already
            // committed, so we can't try and set headers.
            if(keepAlive && !getErrorState().isError() && !isAsync() &&
                    statusDropsConnection(response.getStatus())) {
                setErrorState(ErrorState.CLOSE_CLEAN, null);
            }
        } catch (InterruptedIOException e) {
            setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
        } catch (HeadersTooLargeException e) {
            log.error(sm.getString("http11processor.request.process"), e);
            // The response should not have been committed but check it
            // anyway to be safe
            if (response.isCommitted()) {
                setErrorState(ErrorState.CLOSE_NOW, e);
            } else {
                response.reset();
                response.setStatus(500);
                setErrorState(ErrorState.CLOSE_CLEAN, e);
                response.setHeader("Connection", "close"); // TODO: Remove
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString("http11processor.request.process"), t);
            // 500 - Internal Server Error
            response.setStatus(500);
            setErrorState(ErrorState.CLOSE_CLEAN, t);
            getAdapter().log(request, response, 0);
        }
    }

    // Finish the handling of the request
    rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
    if (!isAsync()) {
        // If this is an async request then the request ends when it has
        // been completed. The AsyncContext is responsible for calling
        // endRequest() in that case.
        endRequest();
    }
    rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);

    // If there was an error, make sure the request is counted as
    // and error, and update the statistics counter
    if (getErrorState().isError()) {
        response.setStatus(500);
    }

    if (!isAsync() || getErrorState().isError()) {
        request.updateCounters();
        if (getErrorState().isIoAllowed()) {
            inputBuffer.nextRequest();
            outputBuffer.nextRequest();
        }
    }

    if (!disableUploadTimeout) {
        int soTimeout = endpoint.getConnectionTimeout();
        if(soTimeout > 0) {
            socketWrapper.setReadTimeout(soTimeout);
        } else {
            socketWrapper.setReadTimeout(0);
        }
    }

    rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

    sendfileState = processSendfile(socketWrapper);
}

rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

if (getErrorState().isError() || (endpoint.isPaused() && !isAsync())) {
    return SocketState.CLOSED;
} else if (isAsync()) {
    return SocketState.LONG;
} else if (isUpgrade()) {
    return SocketState.UPGRADING;
} else {
    if (sendfileState == SendfileState.PENDING) {
        return SocketState.SENDFILE;
    } else {
        if (openSocket) {
            if (readComplete) {
                return SocketState.OPEN;
            } else {
                return SocketState.LONG;
            }
        } else {
            return SocketState.CLOSED;
        }
    }
}

这里面代码很长,我做一个简单的总结。有以下几步操作

  • 对请求做一些预处理,想如果说端口未打开,直接报503然后返回等等的操作。
  • 后调用adapter的service方法,这也是真正进入请求处理阶段的入口。
adapter.service()
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);

if (request == null) {
    // Create objects
    request = connector.createRequest();
    request.setCoyoteRequest(req);
    response = connector.createResponse();
    response.setCoyoteResponse(res);

    // Link objects
    request.setResponse(response);
    response.setRequest(request);

    // Set as notes
    req.setNote(ADAPTER_NOTES, request);
    res.setNote(ADAPTER_NOTES, response);

    // Set query string encoding
    req.getParameters().setQueryStringCharset(connector.getURICharset());
}

if (connector.getXpoweredBy()) {
    response.addHeader("X-Powered-By", POWERED_BY);
}

boolean async = false;
boolean postParseSuccess = false;

req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());

try {
    // Parse and set Catalina and configuration specific
    // request parameters
    postParseSuccess = postParseRequest(req, request, res, response);
    if (postParseSuccess) {
        //check valves if we support async
        request.setAsyncSupported(
                connector.getService().getContainer().getPipeline().isAsyncSupported());
        // Calling the container
        connector.getService().getContainer().getPipeline().getFirst().invoke(
                request, response);
    }
    if (request.isAsync()) {
        async = true;
        ReadListener readListener = req.getReadListener();
        if (readListener != null && request.isFinished()) {
            // Possible the all data may have been read during service()
            // method so this needs to be checked here
            ClassLoader oldCL = null;
            try {
                oldCL = request.getContext().bind(false, null);
                if (req.sendAllDataReadEvent()) {
                    req.getReadListener().onAllDataRead();
                }
            } finally {
                request.getContext().unbind(false, oldCL);
            }
        }

        Throwable throwable =
                (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

        // If an async request was started, is not going to end once
        // this container thread finishes and an error occurred, trigger
        // the async error process
        if (!request.isAsyncCompleting() && throwable != null) {
            request.getAsyncContextInternal().setErrorState(throwable, true);
        }
    } else {
        request.finishRequest();
        response.finishResponse();
    }

} catch (IOException e) {
    // Ignore
} finally {
    AtomicBoolean error = new AtomicBoolean(false);
    res.action(ActionCode.IS_ERROR, error);

    if (request.isAsyncCompleting() && error.get()) {
        // Connection will be forcibly closed which will prevent
        // completion happening at the usual point. Need to trigger
        // call to onComplete() here.
        res.action(ActionCode.ASYNC_POST_PROCESS,  null);
        async = false;
    }

    // Access log
    if (!async && postParseSuccess) {
        // Log only if processing was invoked.
        // If postParseRequest() failed, it has already logged it.
        Context context = request.getContext();
        Host host = request.getHost();
        // If the context is null, it is likely that the endpoint was
        // shutdown, this connection closed and the request recycled in
        // a different thread. That thread will have updated the access
        // log so it is OK not to update the access log here in that
        // case.
        // The other possibility is that an error occurred early in
        // processing and the request could not be mapped to a Context.
        // Log via the host or engine in that case.
        long time = System.currentTimeMillis() - req.getStartTime();
        if (context != null) {
            context.logAccess(request, response, time, false);
        } else if (response.isError()) {
            if (host != null) {
                host.logAccess(request, response, time, false);
            } else {
                connector.getService().getContainer().logAccess(
                        request, response, time, false);
            }
        }
    }

    req.getRequestProcessor().setWorkerThreadName(null);

    // Recycle the wrapper request and response
    if (!async) {
        updateWrapperErrorCount(request, response);
        request.recycle();
        response.recycle();
    }
}

这里总结下来是以下的几步。

  • 通过coyote框架生成对应的request response对象。也就是servletrequest转换成httpServletRequest
  • 补充请求头的信息。
  • 解析请求并补充信息,postParseRequest()
  • connector.getService().getContainer().getPipeline().getFirst().invoke( request, response) 真正进去到容器的管道处理并处理请求。
  • 通过request.finishRequest 与 response.finishResponse(刷OutputBuffer中的数据到浏览器) 来完成整个请求

总结

到了这里关于连接池的内容我们就讲完了。总的下来我们看到连接池和配置文件里面的线程池,连接器协议对应上了。他的本质就是接受通过socketStream监听对应IP端口的请求,然后通过nio(默认)进行连接并且讲request response解析成httpServletRequest 和response。一个请求对应一个处理线程。最后讲request和response传到容器的管道里面进行调用。