TOMCAT架构及其工作原理


前言

因为现在项目都是套框架,让程序员忽略了很多本质的东西,比如一个请求来了它到底怎么运转的,我们在何处可以对它进行加工处理等等,想搞懂这个就需要从源头Tomcat开始,因此就有了这篇文章。
本文是依据Tomcat-9.0.58版本编写,默认使用的是Nio模式。


一、目录结构

目录结构

  • bin: 主要包含可执行程序及脚本
  • conf: 配置文件目录,以下是经常会使用到的配置文件
    - context.xml: 应用上下文配置文档,多个应用程序可以配置多个,此文件tomcat会定时扫描,因此不用重启tomcat就可以添加新应用.
    - server.xml: tomcat服务器配置文档,用以详细配置和使用各组件,如配置service、 connector、Engine、host、context等
    - web.xml: web应用程序配置文档,主要配置listener、filter以及servlet等
  • lib:依赖包文目录
  • logs: 日志存放目录
  • temp: 临时文件目录
  • webapps: 应用程序存放目录,可直接存放war包,tomcat会自动解压
  • work: tomcat工作目录,用来存放解析后的class文件等

注:context.xml是server层面,如果想从host层面进行新增应用的话需要在conf文件夹中新增文件夹,完整路径如下:conf/Catalina/${hostName}/context.xml

二、组件架构

组件架构图

  • Server: 服务器,代表整个 Catalina servlet 容器,它的属性就是整个 servlet 容器的特征
  • Service: 服务,在Server 中存在一个或多个,内部包含一个或多个Connector和一个Engine,有Service才能够向外提供服务
  • Connector: 连接器,在Service中存在一个或者多个,每个连接器中都必须存在端口和协议,连接器的作用是监听端口接受外部连接请求,封装请求交给Engine处理,以及返回Engine处理后的响应结果
  • Engine: 引擎也被称为Container,在Service中只能存在一个,它接收并处理来自一个或多个连接器的所有请求,并将完成的响应返回给连接器
  • Host: 虚拟主机,类似域名,在Engine中存在一个或多个。
  • Context: 上下文,在Host中存在一个或多个,每个Context都代表一个Web 应用程序。

三、工作原理

由组件架构图可知,tomcat 工作的核心组件是service 中的ConnectorEngine ,简单来说就是Connector 监听端口然后读取socket 封装成request,response 交给Engine 处理,Engine 处理完后,Connector 回写流给客户端,下面详细的讲一下这两个重要的组件。

1、Connector

参考 Tomcat官方启动时序图可知,程序入口是Bootstrap中的main方法,下面看看这个main方法:

    public static void main(String args[]) {

        synchronized (daemonLock) {
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            } else {
                // When running as a service the call to stop will be on a new
                // thread so make sure the correct class loader is used to
                // prevent a range of class not found exceptions.
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
        }

        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable t) {
            // Unwrap the Exception for clearer error reporting
            if (t instanceof InvocationTargetException &&
                    t.getCause() != null) {
                t = t.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }

从代码中可以看出main方法中主要涉及到流程的方法是load()start()。其中load() 方法通过反射会调用Catalina.load() 方法,然后Catalina.load()方法会初始化一个Server出来,并且调用server.init() 方法,然后一层一层往下掉用,start() 方法和load 的方法类似也是一层一层调用下来,为了方便大家看的明白特地画了个时序图。
时序图
中间过程就不看了,重点看下NioEndPoint 里面的start() 方法,start() 方法主要是掉用startInternal() 方法.

	/**
     * Start the NIO endpoint, creating acceptor, poller threads.
     */
    @Override
    public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;

            if (socketProperties.getProcessorCache() != 0) {
                processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getProcessorCache());
            }
            if (socketProperties.getEventCache() != 0) {
                eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getEventCache());
            }
            if (socketProperties.getBufferPool() != 0) {
                nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getBufferPool());
            }

            // Create worker collection
            if (getExecutor() == null) {
                createExecutor();
            }

            initializeConnectionLatch();

            // Start poller thread
            poller = new Poller();
            Thread pollerThread = new Thread(poller, getName() + "-Poller");
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();

            startAcceptorThread();
        }
    }

startInternal() 方法主要做了以下几件事:

  • 如果Server.xml 中没有配置线程池的话,此时会初始化一个工作线程池.
  • 始化默认最大连接数,
  • 创建一个Poller 线程并启动
  • 创建一个Acceptor 线程并启动

那么Poller 线程和Acceptor 线程是干啥的呢 ?接着往下看。

public class Acceptor<U> implements Runnable {
    .....
    @Override
    public void run() {

        int errorDelay = 0;
        long pauseStart = 0;

        try {
            // Loop until we receive a shutdown command
            while (!stopCalled) {
                while (endpoint.isPaused() && !stopCalled) {
                    if (state != AcceptorState.PAUSED) {
                        pauseStart = System.nanoTime();
                        // Entered pause state
                        state = AcceptorState.PAUSED;
                    }
                    if ((System.nanoTime() - pauseStart) > 1_000_000) {
                        // Paused for more than 1ms
                        try {
                            if ((System.nanoTime() - pauseStart) > 10_000_000) {
                                Thread.sleep(10);
                            } else {
                                Thread.sleep(1);
                            }
                        } catch (InterruptedException e) {
                            // Ignore
                        }
                    }
                }

                if (stopCalled) {
                    break;
                }
                state = AcceptorState.RUNNING;

                try {
                    //if we have reached max connections, wait
                    endpoint.countUpOrAwaitConnection();

                    // Endpoint might have been paused while waiting for latch
                    // If that is the case, don't accept new connections
                    if (endpoint.isPaused()) {
                        continue;
                    }

                    U socket = null;
                    try {
                        // Accept the next incoming connection from the server
                        // socket
                        socket = endpoint.serverSocketAccept();
                    } catch (Exception ioe) {
                        // We didn't get a socket
                        endpoint.countDownConnection();
                        if (endpoint.isRunning()) {
                            // 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 (!stopCalled && !endpoint.isPaused()) {
                        // setSocketOptions() will hand the socket off to
                        // an appropriate processor if successful
                        if (!endpoint.setSocketOptions(socket)) {
                            endpoint.closeSocket(socket);
                        }
                    } else {
                        endpoint.destroySocket(socket);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    String msg = sm.getString("endpoint.accept.fail");
                    // APR specific.
                    // Could push this down but not sure it is worth the trouble.
                    if (t instanceof Error) {
                        Error e = (Error) t;
                        if (e.getError() == 233) {
                            // Not an error on HP-UX so log as a warning
                            // so it can be filtered out on that platform
                            // See bug 50273
                            log.warn(msg, t);
                        } else {
                            log.error(msg, t);
                        }
                    } else {
                            log.error(msg, t);
                    }
                }
            }
        } finally {
            stopLatch.countDown();
        }
        state = AcceptorState.ENDED;
    }
 }

    // NioEndpoint
	protected boolean setSocketOptions(SocketChannel socket) {
        NioSocketWrapper socketWrapper = null;
        try {
            // Allocate channel and wrapper
            NioChannel channel = null;
            if (nioChannels != null) {
                channel = nioChannels.pop();
            }
            if (channel == null) {
                SocketBufferHandler bufhandler = new SocketBufferHandler(
                        socketProperties.getAppReadBufSize(),
                        socketProperties.getAppWriteBufSize(),
                        socketProperties.getDirectBuffer());
                if (isSSLEnabled()) {
                    channel = new SecureNioChannel(bufhandler, this);
                } else {
                    channel = new NioChannel(bufhandler);
                }
            }
            NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
            channel.reset(socket, newWrapper);
            connections.put(socket, newWrapper);
            socketWrapper = newWrapper;

            // Set socket properties
            // Disable blocking, polling will be used
            socket.configureBlocking(false);
            if (getUnixDomainSocketPath() == null) {
                socketProperties.setProperties(socket.socket());
            }

            socketWrapper.setReadTimeout(getConnectionTimeout());
            socketWrapper.setWriteTimeout(getConnectionTimeout());
            socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
            poller.register(socketWrapper);
            return true;
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            try {
                log.error(sm.getString("endpoint.socketOptionsError"), t);
            } catch (Throwable tt) {
                ExceptionUtils.handleThrowable(tt);
            }
            if (socketWrapper == null) {
                destroySocket(socket);
            }
        }
        // Tell to close the socket if needed
        return false;
    }
	public void register(final NioSocketWrapper socketWrapper) {
            socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
            PollerEvent event = null;
            if (eventCache != null) {
                event = eventCache.pop();
            }
            if (event == null) {
                event = new PollerEvent(socketWrapper, OP_REGISTER);
            } else {
                event.reset(socketWrapper, OP_REGISTER);
            }
            addEvent(event);
        }

Acceptor 中的 run 方法中调用了两个比较重要的方法,其一就是endpoint.serverSocketAccept() 顾名思义就是接受客户端connection ,然后创建一个针对这个连接的SocketChannel 通道,一个连接一个SocketChannel,后续request 数据和response 数据返回都是通过SocketChannel 进行传输,另外SocketChannel 是双向的,这就意味着可以支持同时读写;第二个就是endpoint.setSocketOptions(socket),它的作用是将SocketChannel 包装放入PollerEvent 然后将OP_REGISTER 事件注册到Poller 中,Poller 则会将这个SocketChannel 注册到select 中 。接着就到Poller 线程了,Poller 线程干了啥呢,接着往下看。

public class Poller implements Runnable {
        .......
		@Override
        public void run() {
            // Loop until destroy() is called
            while (true) {

                boolean hasEvents = false;

                try {
                    if (!close) {
                        hasEvents = events();
                        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.close();
                        } catch (IOException ioe) {
                            log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                        }
                        break;
                    }
                    // Either we timed out or we woke up, process events first
                    if (keyCount == 0) {
                        hasEvents = (hasEvents | events());
                    }
                } catch (Throwable x) {
                    ExceptionUtils.handleThrowable(x);
                    log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
                    continue;
                }

                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();
                    iterator.remove();
                    NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (socketWrapper != null) {
                        processKey(sk, socketWrapper);
                    }
                }

                // Process timeouts
                timeout(keyCount,hasEvents);
            }

            getStopLatch().countDown();
        }

		protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
            try {
                if (close) {
                    cancelledKey(sk, socketWrapper);
                } else if (sk.isValid()) {
                    if (sk.isReadable() || sk.isWritable()) {
                        if (socketWrapper.getSendfileData() != null) {
                            processSendfile(sk, socketWrapper, false);
                        } else {
                            unreg(sk, socketWrapper, sk.readyOps());
                            boolean closeSocket = false;
                            // Read goes before write
                            if (sk.isReadable()) {
                                if (socketWrapper.readOperation != null) {
                                    if (!socketWrapper.readOperation.process()) {
                                        closeSocket = true;
                                    }
                                } else if (socketWrapper.readBlocking) {
                                    synchronized (socketWrapper.readLock) {
                                        socketWrapper.readBlocking = false;
                                        socketWrapper.readLock.notify();
                                    }
                                } else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
                                    closeSocket = true;
                                }
                            }
                            if (!closeSocket && sk.isWritable()) {
                                if (socketWrapper.writeOperation != null) {
                                    if (!socketWrapper.writeOperation.process()) {
                                        closeSocket = true;
                                    }
                                } else if (socketWrapper.writeBlocking) {
                                    synchronized (socketWrapper.writeLock) {
                                        socketWrapper.writeBlocking = false;
                                        socketWrapper.writeLock.notify();
                                    }
                                } else if (!processSocket(socketWrapper, SocketEvent.OPEN_WRITE, true)) {
                                    closeSocket = true;
                                }
                            }
                            if (closeSocket) {
                                cancelledKey(sk, socketWrapper);
                            }
                        }
                    }
                } else {
                    // Invalid key
                    cancelledKey(sk, socketWrapper);
                }
            } catch (CancelledKeyException ckx) {
                cancelledKey(sk, socketWrapper);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("endpoint.nio.keyProcessingError"), t);
            }
        }
 }    

	// AbstractEndpoint
	public boolean processSocket(SocketWrapperBase<S> socketWrapper,
            SocketEvent event, boolean dispatch) {
        try {
            if (socketWrapper == null) {
                return false;
            }
            SocketProcessorBase<S> sc = null;
            if (processorCache != null) {
                sc = processorCache.pop();
            }
            if (sc == null) {
                sc = createSocketProcessor(socketWrapper, event);
            } else {
                sc.reset(socketWrapper, event);
            }
            Executor executor = getExecutor();
            if (dispatch && executor != null) {
                executor.execute(sc);
            } else {
                sc.run();
            }
        } catch (RejectedExecutionException ree) {
            getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
            return false;
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            getLog().error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

Poller 线程是事件驱动的,基于IO多路复用原理,当任意一个SocketChannel 中的数据准备好了就会select 就会遍历所有SocketChannel 找到数据准备好的SocketChannel 走后续流程,否则阻塞。后续流程主要掉用 processKey 方法,它主要的作用是根据socketWrapperSocketEvent构建一个 processor 线程交给工作线程池执行。SocketProcessor 线程 执行方法如下:

protected class SocketProcessor extends SocketProcessorBase<NioChannel> {

        ......
        @Override
        protected void doRun() {
            /*
             * Do not cache and re-use the value of socketWrapper.getSocket() in
             * this method. If the socket closes the value will be updated to
             * CLOSED_NIO_CHANNEL and the previous value potentially re-used for
             * a new connection. That can result in a stale cached value which
             * in turn can result in unintentionally closing currently active
             * connections.
             */
            Poller poller = NioEndpoint.this.poller;
            if (poller == null) {
                socketWrapper.close();
                return;
            }

            try {
                int handshake = -1;
                try {
                    if (socketWrapper.getSocket().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 = socketWrapper.getSocket().handshake(event == SocketEvent.OPEN_READ, event == SocketEvent.OPEN_WRITE);
                        // 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) {
                        poller.cancelledKey(getSelectionKey(), socketWrapper);
                    }
                } else if (handshake == -1 ) {
                    getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);
                    poller.cancelledKey(getSelectionKey(), socketWrapper);
                } else if (handshake == SelectionKey.OP_READ){
                    socketWrapper.registerReadInterest();
                } else if (handshake == SelectionKey.OP_WRITE){
                    socketWrapper.registerWriteInterest();
                }
            } catch (CancelledKeyException cx) {
                poller.cancelledKey(getSelectionKey(), socketWrapper);
            } catch (VirtualMachineError vme) {
                ExceptionUtils.handleThrowable(vme);
            } catch (Throwable t) {
                log.error(sm.getString("endpoint.processing.fail"), t);
                poller.cancelledKey(getSelectionKey(), socketWrapper);
            } finally {
                socketWrapper = null;
                event = null;
                //return to cache
                if (running && processorCache != null) {
                    processorCache.push(this);
                }
            }
        }

SocketProcessor 线程主要的作用是 判断SocketChannel 是否读写流准备好了,如果没有准备好就添加事件到Poller 线程中,否则掉用 getHandler().process() 方法,这个方法最终会调用到具体的processor 类中的service 方法如Http11Processor 中的service 方法

public class Http11Processor extends AbstractProcessor {
	public SocketState service(SocketWrapperBase<?> socketWrapper){
		......
		getAdapter().service(request, response);
		......
	}
}

Http11Processor 也是中间过程会继续往下调用,此时就到了CoyoteAdapter.service(org.apache.coyote.Request req, org.apache.coyote.Response res) 方法中了

public class CoyoteAdapter implements Adapter {
	public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)throws Exception {
 		......
 		// Calling the container
        connector.getService().getContainer().getPipeline().getFirst().invoke(
                        request, response);
 		......
	}
}

CoyoteAdapter 适配器主要的作用是将Http11Processor 中传递过来的requestresponse 进行封装,交给Engine 处理,自此在Connector 中前半部分已经结束了,最后来个总结,为了大家看的明白画了个图。
在这里插入图片描述

  • Acceptor : 单线程,采用SocketChannel 通道原理读取socket ,然后将SocketChannel 封装到PollerEvent对象 ,然后注册事件到Poller 中。Acceptor是事件的生产者。
  • Poller : 单线程,事件驱动,基于IO多路复用原理 检查是否通道中流准备好了,准备好了就走后续流程,创建SocketProcessor 线程并交给Work ThreadPool 执行。Poller 是事件的消费者。
  • SocketProcessor : 工作线程池调度,作用找到相应的Processor 处理,最终找到CoyoteAdapter 适配器 ,封装requestresponse 交给Engine 处理,最后也是由CoyoteAdapter 适配器调用方法将response 流回写到SocketChannel中,这样客户端就可以得到服务器的反馈内容了。

注意:默认是NioChannel ,支持双向即可以同时读写,但是多个读在同一个NioChannel还是是阻塞模式。

2、Engine

Engine 简单来说就是处理请求的,也就是掉用具体servletservice 方法.
由上一步可知最后执行到这里:connector.getService().getContainer().getPipeline().getFirst().invoke(request, response); 解释一下这些方法是获取的都是什么。

  • getService() : StandardService
  • getContainer() : StandardEngine
  • getPipeline() : StandardPipeline
  • getFirst() : StandardEngineValve

那么代码走着

final class StandardEngineValve extends ValveBase {

    .........
    @Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Select the Host to be used for this Request
        Host host = request.getHost();
        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()) {
                response.sendError(404);
            }
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);
    }
}

后续context、wrapper 步骤都是invoke 因此就不在贴代码了
context.getPipeline().getFirst().invoke(request, response);
wrapper.getPipeline().getFirst().invoke(request, response);

最后重点贴一下StandardWrapperValve.invoke() 方法

//StandardWrapperValve
public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        .......
        // Create the filter chain for this request
        ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
		filterChain.doFilter
                            (request.getRequest(), response.getResponse());
        .......
    }
    //ApplicationFilterChain
   	private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {

        // Call the next filter if there is one
        if (pos < n) {
            ApplicationFilterConfig filterConfig = filters[pos++];
            try {
                Filter filter = filterConfig.getFilter();

                if (request.isAsyncSupported() && "false".equalsIgnoreCase(
                        filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
                }
                if( Globals.IS_SECURITY_ENABLED ) {
                    final ServletRequest req = request;
                    final ServletResponse res = response;
                    Principal principal =
                        ((HttpServletRequest) req).getUserPrincipal();

                    Object[] args = new Object[]{req, res, this};
                    SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
                } else {
                    filter.doFilter(request, response, this);
                }
            } catch (IOException | ServletException | RuntimeException e) {
                throw e;
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                throw new ServletException(sm.getString("filterChain.filter"), e);
            }
            return;
        }

        // We fell off the end of the chain -- call the servlet instance
        try {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(request);
                lastServicedResponse.set(response);
            }

            if (request.isAsyncSupported() && !servletSupportsAsync) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                        Boolean.FALSE);
            }
            // Use potentially wrapped request from this point
            if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse) &&
                    Globals.IS_SECURITY_ENABLED ) {
                final ServletRequest req = request;
                final ServletResponse res = response;
                Principal principal =
                    ((HttpServletRequest) req).getUserPrincipal();
                Object[] args = new Object[]{req, res};
                SecurityUtil.doAsPrivilege("service",
                                           servlet,
                                           classTypeUsedInService,
                                           args,
                                           principal);
            } else {
                servlet.service(request, response);
            }
        } catch (IOException | ServletException | RuntimeException e) {
            throw e;
        } catch (Throwable e) {
            e = ExceptionUtils.unwrapInvocationTargetException(e);
            ExceptionUtils.handleThrowable(e);
            throw new ServletException(sm.getString("filterChain.servlet"), e);
        } finally {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(null);
                lastServicedResponse.set(null);
            }
        }
    } 

StandardWrapperValve.invoke() 方法最终会调用到ApplicationFilterChain.internalDoFilter() 方法。ApplicationFilterChain.internalDoFilter() 方法主要干了两件事

  • 过滤链:责任链设计模式从filters 取出传递过滤
  • 调用servletsevice(request, response) 方法

处理完成后会一层一层返回至CoyoteAdapter

public class CoyoteAdapter implements Adapter {
	public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)throws Exception {
 		......
 		// Calling the container
        connector.getService().getContainer().getPipeline().getFirst().invoke(
                        request, response);
       
        ......
          request.finishRequest();
          response.finishResponse();
 		......
	}
}

CoyoteAdapter 会调用 response.finishResponse() 方法将response流通过SocketChannel 回写给客户端,具体代码就不贴了,有兴趣自己可以查看Tomcat 源码。最后画个图总结一下。
在这里插入图片描述

总结

本文只是简单Connector加载过程及请求流转过程,忽略很多不涉及流程的步骤,如需深究请查看源码

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值