Tomcat的请求处理流程

Tomcat的启动流程一文中分析到在AbstractEndpoint中会创建服务端socket并绑定地址进行监听,但没有涉及当请求进来之后Tomcat是怎么处理的。本文就来分析一下该问题。


本文以默认使用的Http11NioProtocol这个协议处理器为例,其他协议处理器也可以参考,大体思路是相似的。

网络事件模型

NioEndpoint组件的启动过程中,会创建并启动Acceptor线程和Poller线程,这两个类都是NioEndpoint中的内部类,下面分别看看它们的run方法做了什么事情。

Acceptor

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
@Override
public void run() {

    int errorDelay = 0;

    // Loop until we receive a shutdown command
    // 事件循环
    while (running) {

        // Loop if endpoint is paused
        // 如果需要暂停运行状态
        while (paused && running) {
            state = AcceptorState.PAUSED;
            try {
                // 暂停50毫秒
                Thread.sleep(50);
            } catch (InterruptedException e) {
                // Ignore
            }
        }

        // 结束运行则退出循环
        if (!running) {
            break;
        }
        state = AcceptorState.RUNNING;

        try {
            //if we have reached max connections, wait
            // 下面准备接收新连接的,先让门闩加1;如果已经达到最大值,则阻塞等待。
            countUpOrAwaitConnection();

            SocketChannel socket = null;
            try {
                // Accept the next incoming connection from the server
                // socket
                /*
                 * 接收客户端的连接,在bind方法中,serverSock被设置为阻塞模式,所以这里会阻塞直到有客户端连接。
                 * 如果有多个Acceptor,怎么保证一个请求不被多个acceptor线程处理了?
                 */
                socket = serverSock.accept();
            } catch (IOException ioe) {
                // We didn't get a socket
                // 没有获取到socket,则让门闩减1
                countDownConnection();
                if (running) {
                    // Introduce delay if necessary
                    // 处理异常
                    errorDelay = handleExceptionWithDelay(errorDelay);
                    // re-throw
                    throw ioe;
                } else {
                    break;
                }
            }
            // Successful accept, reset the error delay
            errorDelay = 0;

            // Configure the socket
            if (running && !paused) {
                // setSocketOptions() will hand the socket off to
                // an appropriate processor if successful
                /*
                 * 通过setSocketOptions处理客户端的连接请求。
                 */
                if (!setSocketOptions(socket)) {
                    closeSocket(socket);
                }
            } else {
                closeSocket(socket);
            }
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString("endpoint.accept.fail"), t);
        }
    }
    state = AcceptorState.ENDED;
}

通过调用底层JDK的ServerSocketChannel类的accept方法来获取连接,由于在NioEndpointbind方法中是设置的阻塞模式,所以这里如果没有请求到来则会阻塞在这里。接受到请求后,会调用setSocketOptions方法来处理请求。

v8.5.59
setSocketOptions
setProperties
<
>
java
java/org/apache/tomcat/util/net/NioEndpoint.java
protected boolean setSocketOptions(SocketChannel socket) {
    // Process the connection
    try {
        //disable blocking, APR style, we are gonna be polling it
        // 和serverSocket不同,客户端连接的socket设置为非阻塞模式。
        socket.configureBlocking(false);
        // 获取socket对象
        Socket sock = socket.socket();
        // 设置socket的一些属性
        socketProperties.setProperties(sock);

        /*
         * nioChannels是一个队列,用于缓存客户端的连接通道对象,避免每次新进来连接就创建。
         * 这里的NioChannel是Tomcat中对网络通道的封装。
         */
        NioChannel channel = nioChannels.pop();
        if (channel == null) { // 如果没获取到缓存的通道对象,则新建
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                    socketProperties.getAppReadBufSize(),
                    socketProperties.getAppWriteBufSize(),
                    socketProperties.getDirectBuffer());
            // 新建通道对象, 并将channel和表示客户端连接的socket关联
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(socket, bufhandler);
            }
        } else { // 如果有缓存的通道,那么将其和当前的socket关联
            channel.setIOChannel(socket);
            // 重置一些属性
            channel.reset();
        }
        // 将channel封装为PollEvent,并加入到队列中,poller线程会处理。
        getPoller0().register(channel);
    }
    return true;
}
java
java/org/apache/tomcat/util/net/SocketProperties.java
public void setProperties(Socket socket) throws SocketException{
    if (rxBufSize != null) // 设置接收缓冲区大小
        socket.setReceiveBufferSize(rxBufSize.intValue());
    if (txBufSize != null) // 设置发送缓冲区大小
        socket.setSendBufferSize(txBufSize.intValue());
    if (ooBInline !=null) // 选项开启时,带外数据将被留在正常的输入队列中
        socket.setOOBInline(ooBInline.booleanValue());
    if (soKeepAlive != null) // TCP保活机制
        socket.setKeepAlive(soKeepAlive.booleanValue());
    if (performanceConnectionTime != null && performanceLatency != null &&
            performanceBandwidth != null)
        // 设置性能选项
        socket.setPerformancePreferences(
                performanceConnectionTime.intValue(),
                performanceLatency.intValue(),
                performanceBandwidth.intValue());
    if (soReuseAddress != null) // 设置地址重用
        socket.setReuseAddress(soReuseAddress.booleanValue());
    if (soLingerOn != null && soLingerTime != null)
        // 设置在关闭socket时是否等数据发送完,第二个参数是最多等待的时间
        socket.setSoLinger(soLingerOn.booleanValue(),
                soLingerTime.intValue());
    if (soTimeout != null && soTimeout.intValue() >= 0)
        // 设置接收数据的超时时间
        socket.setSoTimeout(soTimeout.intValue());
    if (tcpNoDelay != null) {
        try {
            // 是否禁用TCP的Nagle算法
            socket.setTcpNoDelay(tcpNoDelay.booleanValue());
        } catch (SocketException e) {
            // Some socket types may not support this option which is set by default
        }
    }
}

最重要的是会选择一个poller线程,并将请求注册到它的队列中,供poller线程处理。先来看看选择poller的算法。

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
public Poller getPoller0() {
    /*
     * 这里采用的是轮询法,通过自增取余来实现,和Netty中的GenericEventExecutorChooser类似,当然这里是在选poller,而不是worker。
     * 采用Math.abs是为了防止自增导致的溢出,确保下标是合法的。
     */
    int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
    return pollers[idx];
}

这里是直接使用的轮询法,通过自增取余来实现。然后Acceptor线程会将请求注册到poller线程中。

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
private final SynchronizedQueue<PollerEvent> events =
        new SynchronizedQueue<>();
private void addEvent(PollerEvent event) {
    events.offer(event);
    if ( wakeupCounter.incrementAndGet() == 0 ) selector.wakeup();
}
/*
 * 虽然该方法���Poller类中,但是是被Acceptor调用的。
 * 用于实现Acceptor和Poller两种线程的交互。
 */
public void register(final NioChannel socket) {
    socket.setPoller(this);
    // 创建wrapper对象,用于包装NioChannel和NioEndpoint对象。
    NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
    socket.setSocketWrapper(ka);
    // 设置poller为this,后续在selector中注册的时候会获取
    ka.setPoller(this);
    // 设置socket的一些属性
    ka.setReadTimeout(getSocketProperties().getSoTimeout());
    ka.setWriteTimeout(getSocketProperties().getSoTimeout());
    ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
    ka.setReadTimeout(getConnectionTimeout());
    ka.setWriteTimeout(getConnectionTimeout());
    /*
     * 和NioChannel类似,这里从对象池中获取PollerEvent。
     * PollerEvent(简称PE)是Acceptor和Poller交互的媒介。
     * Acceptor收到请求后会调用该方法封装为PollerEvent,然后添加到队列中。
     * Poller线程从队列中获取PE,然后进行处理。
     */
    PollerEvent r = eventCache.pop();
    // 设置��兴趣的事件为读
    ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    // 如果没缓存,则创建pe对象,事件类型是REGISTER。后续在PollerEvent的run方法中会处理。
    if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
    // 如果有缓存,则重置数据,注意事件类型是REGISTER。
    else r.reset(socket,ka,OP_REGISTER);
    /*
     * 将pe对象添加到队列中,poller线程会从队列中取出来处理。
     * 至此,Acceptor线程完成任务,接下来就是Poller线程处理任务了。
     */
    addEvent(r);
}

在该方法中主要做了下面几件事情:

  • 将请求封装为NioSocketWrapper类型的对象ka;
  • 将ka对象封装到PollerEvent对象r中;
  • 将事件对象r添加到队列中;

注意,尽管该方法是Poller类中的方法,但是执行该方法的还是Acceptor线程,执行完该方法后,Acceptor的针对该请求的任务就结束了,又回到事件循环中准备接收下一个连接。

Poller线程

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
@Override
public void run() {
    // Loop until destroy() is called

    // 事件循环,不断处理队列中的事件
    while (true) {

        boolean hasEvents = false;

        try {
            if (!close) {
                // 处理队列中的事件
                hasEvents = events();
                // 执行select操作,获取已就绪的IO事件。
                if (wakeupCounter.getAndSet(-1) > 0) {
                    //if we are here, means we have other stuff to do
                    //do a non blocking select
                    keyCount = selector.selectNow();
                } else {
                    keyCount = selector.select(selectorTimeout);
                }
                wakeupCounter.set(0);
            }
            if (close) {
                // 结束运行之前先把队列中的任务执行完
                events();
                timeout(0, false);
                try {
                    // 关闭selector
                    selector.close();
                } catch (IOException ioe) {
                    log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                }
                break;
            }
        } catch (Throwable x) {
            ExceptionUtils.handleThrowable(x);
            log.error("",x);
            continue;
        }
        //either we timed out or we woke up, process events first
        // hasEvents记录是否处理了事件
        if ( keyCount == 0 ) hasEvents = (hasEvents | events());

        // 如果有已就绪的事件,那么获取迭代器,准备迭代并处理。
        Iterator<SelectionKey> iterator =
            keyCount > 0 ? selector.selectedKeys().iterator() : null;
        // Walk through the collection of ready keys and dispatch
        // any active event.

        // 遍历已就绪事件
        while (iterator != null && iterator.hasNext()) {
            SelectionKey sk = iterator.next();
            NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
            // Attachment may be null if another thread has called
            // cancelledKey()
            if (attachment == null) {
                iterator.remove();
            } else {
                iterator.remove();
                // 处理IO事件
                processKey(sk, attachment);
            }
        }//while

        //process timeouts
        timeout(keyCount,hasEvents);
    }//while

    // 对stop门闩减1
    getStopLatch().countDown();
}

通过该方法可以看出Poller线程中主要做两件事件:

  • 处理队列中的任务;
  • 处理selector中的就绪事件;

注册新连接

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
public boolean events() {
    boolean result = false;

    PollerEvent pe = null;
    // 从队列中获取PE对象
    for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
        result = true;
        try {
            // 执行pe
            pe.run();
            // 重置pe对象
            pe.reset();
            if (running && !paused) {
                // 将pe加入到对象池中
                eventCache.push(pe);
            }
        } catch ( Throwable x ) {
            log.error("",x);
        }
    }

    return result;
}

该方法主要在调用PollerEvent事件对象,该类实现了Runnable接口,但是在该方法中,是直接调用的其run方法。

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
@Override
public void run() {
    if (interestOps == OP_REGISTER) { // 如果是注册操作
        try {
            /*
             * 将客户端的连接请求注册到selector中,感兴趣的事件是读,attachment是wrapper对象。
             * tomcat中最多只有2个poller,这里是每个poller都有一个selector,获取的是当前Poller线程中的selector。
             */
            socket.getIOChannel().register(
                    socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
        } catch (Exception x) {
            log.error(sm.getString("endpoint.nio.registerFail"), x);
        }
    } else { // 其他类型的操作
    }
}

在该方法中,如果发现是需要注册的新连接,那么会将请求注册到selector中,这样后续在Poller线程的事件循环中就能处理了。

处理请求

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
    try {
        // 如果poller线程被关闭,
        if ( close ) {
            // 则取消key
            cancelledKey(sk);
        } else if ( sk.isValid() && attachment != null ) {
            // 判断就绪的事件是不是读写事件
            if (sk.isReadable() || sk.isWritable() ) {
                // 如果使用sendfile
                if ( attachment.getSendfileData() != null ) {
                    // 处理sendfile
                    processSendfile(sk,attachment, false);
                } else {
                    unreg(sk, attachment, sk.readyOps());
                    boolean closeSocket = false;
                    // Read goes before write
                    // 读事件
                    if (sk.isReadable()) {
                        // 处理客户端的请求
                        if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
                            closeSocket = true;
                        }
                    }
                    // 写事件
                    if (!closeSocket && sk.isWritable()) {
                        if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
                            closeSocket = true;
                        }
                    }
                    // 如果处理读写事件失败
                    if (closeSocket) {
                        cancelledKey(sk);
                    }
                }
            }
        } else {
            //invalid key
            cancelledKey(sk);
        }
    }
}

在该方法中会调用processSocket来处理请求。

v8.5.59
java
java/org/apache/tomcat/util/net/AbstractEndpoint.java
public boolean processSocket(SocketWrapperBase<S> socketWrapper,
        SocketEvent event, boolean dispatch) {
    try {
        if (socketWrapper == null) {
            return false;
        }
        // 同样地,从对象池中获取对象。
        SocketProcessorBase<S> sc = processorCache.pop();
        if (sc == null) {
            // 如果没有缓存,则创建新对象。不同的子类,创建不同类型的SocketProcessorBase实现。
            sc = createSocketProcessor(socketWrapper, event);
        } else {
            sc.reset(socketWrapper, event);
        }
        /*
         * 获取线程池对象,虽然可能poller线程有多个, 每个poller线程又有自己单独的selector。
         * 但是线程池对象是属于endpoint的,所以多个poller线程对应一个线程池。
         */
        Executor executor = getExecutor();
        if (dispatch && executor != null) {
            // 提交到线程池中处理
            executor.execute(sc);
        } else {
            // 如果没有线程池,则直接在poller线程中执行。
            sc.run();
        }
    }
    return true;
}

在该方法中,会把SocketWrapperBase对象封装到SocketProcessorBase对象中,然后把后者提交到线程池中执行。

至此Poller线程针对该请求的任务就结束了,后续的操作都是在线程池中进行的。

SocketProcessorBase

v8.5.59
java
java/org/apache/tomcat/util/net/SocketProcessorBase.java
@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();
    }
}
protected abstract void doRun();

doRun方法由子类实现,这里仍然参考NioEndpoing中的内部类SocketProcessor的实现。

v8.5.59
java
java/org/apache/tomcat/util/net/NioEndpoint.java
@Override
protected void doRun() {
    // 获取通道对象
    NioChannel socket = socketWrapper.getSocket();
    // 获取在poller线程的selector中的注册键
    SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());

    try {
        if (handshake == 0) { // 处理正常请求
            SocketState state = SocketState.OPEN;
            // Process the request from this socket
            /*
             * getHandler返回的是AbstractProtocol中的ConnectionHandler,调用其process进行处理。
             * 执行当前方法的线程是AbstractEndPoint中executor线程池中的线程。
             */
            if (event == null) {
                // 获取handler并处理
                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();
        }
    }
    finally {
        socketWrapper = null;
        event = null;
        //return to cache
        if (running && !paused) {
            processorCache.push(this);
        }
    }
}

在该方法中,主要是通过调用getHanlder来获取handler对象,然后调用其process方法。

getHandler方法主要是返回handler属性,那这个属性又是什么时候设置的呢?在创建ProtocolHandler的时候,在AbstractHttp11Protocol的构造方法中会设置。

v8.5.59
java
java/org/apache/coyote/http11/AbstractHttp11Protocol.java
public AbstractHttp11Protocol(AbstractEndpoint<S> endpoint) {
    super(endpoint);
    // 设置连接超时时间,默认是60秒
    setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
    // 创建并设置连接处理器
    ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
    setHandler(cHandler);
    // 给endpoint对象设置handler,在线程池中会调用到该对象。
    getEndpoint().setHandler(cHandler);
}

可以看到,这里创建的是ConnectionHandler,下面来看看它的process方法。

ConnectionHandler

该类定义在AbstractProtocolProtocolHandler接口的子类)中,实现了AbstractEndpoint的内部接口Handler

v8.5.59
process
createProcessor
<
>
java
java/org/apache/coyote/AbstractProtocol.java
@Override
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
            // 从对象池中获取
            processor = recycledProcessors.pop();
            // 如果没有从对象池中获取到对象,则新建一个。
            processor = getProtocol().createProcessor();
            // 调用处理器的process方法。
            state = processor.process(wrapper, status);
}
java
java/org/apache/coyote/http11/AbstractHttp11Protocol.java
@Override
protected Processor createProcessor() {
    /*
     * 新建协议处理器
     */
    Http11Processor processor = new Http11Processor(this, getEndpoint());
    /*
     * 设置adapter,这里的adapter是在Connector的initInternal方法中创建的,
     * 并调用protocolHandler的setAdapter()方法设置的。
     * 默认是CoyoteAdapter对象。
     */
    processor.setAdapter(getAdapter());
    processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
    processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
    processor.setDisableUploadTimeout(getDisableUploadTimeout());
    processor.setRestrictedUserAgents(getRestrictedUserAgents());
    processor.setMaxSavePostSize(getMaxSavePostSize());
    return processor;
}

ConnectionHandlerprocess方法非常长,我们只关注请求处理部分就行,最终是调用了一个processor的process方法,那这个processor是什么呢。如果缓存中没有processor则会创建,我们根据这个线索去找,在AbstractHttp11Protocol中的createProcessor中找到了答案,就是Http11Processor

Http11Processor是Tomcat中的一个Processor组件,继承体系如下图所示。注意和Http11NioProtocol进行区分,后者是ProtocolHanlder,一个是processor,一个是handler,在handler中会创建并通过processor来处理请求。

Http11Processor

创建

v8.5.59
Http11Processor
AbstractProcessor
<
>
java
java/org/apache/coyote/http11/Http11Processor.java
@SuppressWarnings("deprecation")
public Http11Processor(AbstractHttp11Protocol<?> protocol, AbstractEndpoint<?> endpoint) {
    super(endpoint);
    this.protocol = protocol;

    httpParser = new HttpParser(protocol.getRelaxedPathChars(),
            protocol.getRelaxedQueryChars());

    inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
            protocol.getRejectIllegalHeader(), httpParser);
    request.setInputBuffer(inputBuffer);

    outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize(),
            protocol.getSendReasonPhrase());
    response.setOutputBuffer(outputBuffer);

    // Create and add the identity filters.
    inputBuffer.addFilter(new IdentityInputFilter(protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new IdentityOutputFilter());

    // Create and add the chunked filters.
    inputBuffer.addFilter(new ChunkedInputFilter(protocol.getMaxTrailerSize(),
            protocol.getAllowedTrailerHeadersInternal(), protocol.getMaxExtensionSize(),
            protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new ChunkedOutputFilter());

    // Create and add the void filters.
    inputBuffer.addFilter(new VoidInputFilter());
    outputBuffer.addFilter(new VoidOutputFilter());

    // Create and add buffered input filter
    inputBuffer.addFilter(new BufferedInputFilter());

    // Create and add the gzip filters.
    //inputBuffer.addFilter(new GzipInputFilter());
    outputBuffer.addFilter(new GzipOutputFilter());

    pluggableFilterIndex = inputBuffer.getFilters().length;
}
java
java/org/apache/coyote/AbstractProcessor.java
public AbstractProcessor(AbstractEndpoint<?> endpoint) {
    this(endpoint, new Request(), new Response());
}
ic AbstractProcessor(AbstractEndpoint<?> endpoint) {
this(endpoint, new Request(), new Response());

注意在AbstractProcessor的构造方法中,创建了RequestResponse对象,注意这两个类都是org.apache.coyote包下的,别和HttpServletReqeustHttpServletResponse搞混了。

process

process方法定义在父类AbstractProcessorLight中。

v8.5.59
java
java/org/apache/coyote/AbstractProcessorLight.java
@Override
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
        throws IOException {
            // 如果是读事件,则调用service方法处理。
            state = service(socketWrapper);
    return state;
}
protected abstract SocketState service(SocketWrapperBase<?> socketWrapper) throws IOException;

service方法是个模板方法,下面来看看子类Http11Processor中的实现。该类中的实现也是非常长,这里挑选关键步骤。

v8.5.59
java
java/org/apache/coyote/http11/Http11Processor.java
@Override
public SocketState service(SocketWrapperBase<?> socketWrapper)
    throws IOException {
                /*
                 * 获取适配器,并执行其service方法,参考CoyoteAdapter。
                 * 这里的adapter是在Connector的initInternal方法中创建的,然后设置到protocolHandler中。
                 * 在protocolHandler的createProcessor方法中为新建的Processor对象设置了adapter。
                 */
                getAdapter().service(request, response);
}

这里会调用适配器的service方法,这个适配器是在哪里设置的呢?其实在AbstractHttp11Protocol创建Http11Processor对象后,就立马设置了适配器对象。而AbstractHttp11Protocol中的适配器对象是在Connection组件中的初始化过程中,被创建和设置进来的。该适配器对象是CoyoteAdapter类型的。

CoyoteAdapter

v8.5.59
java
java/org/apache/catalina/connector/CoyoteAdapter.java
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
        throws Exception {

    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);

    /*
     * 第一次请求时,这里为null。
     * 然后新建对象之后,就会通过setNote方法保存起来。
     */
    if (request == null) {
        // Create objects
        /*
         * 这里的connector就是StandardService中的子组件Connector。
         *
         * 这里在通过connector组件来创建request和response对象,分别是HttpServletRequest和HttpServletResponse类型的。
         * Tomcat中connector包下的Request和Response分别实现了HttpServletRequest接口和HttpServletResponse接口。
         * 注意区分req和Request,尽管两者的类名都是Request,但req是coyote包下的,而request是connector包下的。res和response同理。
         *
         * 两组类有什么区别?tomcat为什么要定义两组同名的类?
         */
        request = connector.createRequest();
        request.setCoyoteRequest(req);
        response = connector.createResponse();
        response.setCoyoteResponse(res);

        // Link objects
        // 将request和response互相关联
        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
        /*
         * 映射host, context和wrapper组件
         */
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {

            // Calling the container
            /*
             * 调用pipeline完成请求处理。
             * 这里的getService()方法返回的是StandardService组件。
             * getContainer()方法返回的是Engine组件,是StandardEngine。
             * getPipeline()方法返回的是StandardPipeline,是在ContainerBase类中自动创建的(定义属性的时候就直接new初始化了)。
             * getFirst()方法返回的是Pipeline中的first属性,参考StandardEngine构造器设置的basic属性。如果first为null,则返回basic。
             * invoke()方法是在standardEngineValve中。
             */
            connector.getService().getContainer().getPipeline().getFirst().invoke(
                    request, response);
        }
        else {
            request.finishRequest();
            response.finishResponse();
        }

    } catch (IOException e) {
        // Ignore
    } finally {
    }
}

这里最重要的是在调用Connector的容器子组件,即StandardEngine的流水线pipeline中的Valve

StandardEngineValve

v8.5.59
java
java/org/apache/catalina/core/StandardEngineValve.java
/*
 * 该方法在CoyoteAdapter中的service方法中会被调用。
 */
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Select the Host to be used for this Request
    // 从connector/Request中获取host组件对象
    Host host = request.getHost();
    // 如果找不到host
    if (host == null) {
        // HTTP 0.9 or HTTP 1.0 request without a host when no default host
        // is defined.
        // Don't overwrite an existing error
        if (!response.isError()) {
            // 响应404
            response.sendError(404);
        }
        return;
    }
    // 异步相关
    if (request.isAsyncSupported()) {
        request.setAsyncSupported(host.getPipeline().isAsyncSupported());
    }

    // Ask this Host to process this request
    // 调用host的pipeline处理请求
    host.getPipeline().getFirst().invoke(request, response);
}

这里主要就是在调用Host组件的pipeline中的Valve

StandardHostValve

v8.5.59
java
java/org/apache/catalina/core/StandardHostValve.java
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {
            if (!response.isErrorReportRequired()) {
                // 调用Context的pipeline处理请求。
                context.getPipeline().getFirst().invoke(request, response);
            }
}

StandardContextValve

v8.5.59
java
java/org/apache/catalina/core/StandardContextValve.java
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {
    // 获取wrapper对象。
    Wrapper wrapper = request.getWrapper();
    // 调用Wrapper的pipeline处理请求。
    wrapper.getPipeline().getFirst().invoke(request, response);
}

StandardWrapperValve

v8.5.59
java
java/org/apache/catalina/core/StandardWrapperValve.java
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {
    // 创建过滤器链
    ApplicationFilterChain filterChain =
            ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
                    /*
                     * 执行过滤器链,
                     * 过滤器执行完后调用servlet。
                     */
                    filterChain.doFilter
                        (request.getRequest(), response.getResponse());
}

doFilter方法中,又会调用到internalDoFilter方法。

v8.5.59
java
java/org/apache/catalina/core/ApplicationFilterChain.java
private void internalDoFilter(ServletRequest request,
                              ServletResponse response)
    throws IOException, ServletException {
            // 获取filter对象
            Filter filter = filterConfig.getFilter();
                // 执行filter
                filter.doFilter(request, response, this);
            /*
             * 调用servlet的service方法,进入servlet的领域。
             */
            servlet.service(request, response);
}

在该方法中,先是执行了过滤器,执行完后就会调用Servletservice方法。

至此,请求在Tomcat中的入站部分执行完毕。

总结

本文大致上分析了请求的入站流程,可以发现整个流程涉及的内容还是很多的,为了突出主线,没有介绍太多其他内容,这些旁支内容适合单独写文章来介绍。