Tomcat 9 源码分析(4)— 请求过程原理

Tomcat请求处理

Connector

Connector(连接器)组件是Tomcat最核心的两个组件之一,主要的职责就是负责接收客户端连接和客户端请求的处理加工。每个Connector都将指定一个端口进行监听,分别负责对请求报文的解析和响应报文组装,解析过程生成Request对象,而组装过程涉及Response对象

如果将Tomcat整体比作一个巨大的城堡,那么Connector组件就是城堡的城门,每个人要进入城门就必须通过城门,它为人们进出城堡提供了通道。同时,一个城堡还可能有两个或者多个城门,每个城门代表了不同的通道

Connector整体结构图

在这里插入图片描述

Connector的初始化

Tomcat中有很多容器,包括Server、Service、Connector等,其中Connector正是与HTTP请求处理相关的容器。Service是Server的子容器,而Connector又是Service的子容器。那么这三个容器的初始化顺序为:Server→Service→Connector。Connector的实现分以下几种:

  • Http Connector:基于HTTP协议,负责建立HTTP连接。它又分为BIO Http ConnectorNIO Http Connector两种,后者提供非阻塞IO与长连接Comet支持

  • AJP Connector:基于AJP协议,AJP是专门设计用于Tomcat与HTTP服务器通信定制的协议,能提供较高的通信速度和效率。如与Apache服务器集成时,采用这个协议

  • APR HTTP Connector:用C实现,通过JNI调用。主要提升静态资源(如HTML、图片、CSS、JS等)的访问性能。限你在这个库以独立出来可用在任何项目中。由于APR性能较前两类有很大提升,所以目前是Tomcat的默认

接下来分析Connector 的初始化方法

org.apache.catalina.connector.Connector.initInternal()

@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    if (protocolHandler == null) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
    }

    // Initialize adapter
    // 1.初始化adapter
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    if (service != null) {
        protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
    }

    // Make sure parseBodyMethodsSet has a default
    // 2.设置接收body的method列表,默认为POST
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }

    if (protocolHandler.isAprRequired() && !AprStatus.isInstanceCreated()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
                getProtocolHandlerClassName()));
    }
    if (protocolHandler.isAprRequired() && !AprStatus.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
                getProtocolHandlerClassName()));
    }
    if (AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL() &&
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                (AbstractHttp11JsseProtocol<?>) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &&
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }

    // 3.初始化protocolHandler
    try {
        //  初始化ProtocolHandler
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

Connector.initInternal()内的执行步骤为:

  1. 初始化adapter
  2. 设置接收body的method列表,默认为POST
  3. 初始化protocolHandler
Connector的初始化步骤如下:
构造处理网络协议的CoyoteAdapter

Connector的initInternal方法构造了CoyoteAdapter对象,并将其设置为ProtocolHandler的Adapter。Tomcat处理HTTP请求组,需要有一个ServerSocket监听网络端口来完成任务,接口ProtocolHandler便是被设计成控制网络端口金婷组件运行,负责组件的生命周期控制,这个接口实际并没有定义网络端口监听功能的规范,而是用于负责维护组件的生命周期。从ProtocolHandler的名字来看,它应该是网络协议的处理者,但它实际上不负责这个功能,而是将其交给org.apache.coyote.Adapter来完成,这么设计是为了方便维护和拓展新功能。Http11Protocol是ProtocolHandler接口的一个实现(是Connector的默认处理协议),被设计用来处理HTTP1.1网络协议的请求,通过该类可以完成在某个网络端口上面的监听,同时以HTTP1.1的协议来解析请求内容,然后将请求传递到Connector所寄居的Container容器pipeline流水工作线上处理。

ProtocolHandler的生成则是当Tomcat初始化执行Bootstrap.load()的时候,使用Digester解析标签的时候,会执行startElement方法,startElement中会调用Rule的begin()方法,Connector对应的Rule包括ConnectorCreateRule

org.apache.catalina.startup.ConnectorCreateRule.begin()

public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
    Service svc = (Service) digester.peek();
    Executor ex = null;
    String executorName = attributes.getValue("executor");
    if (executorName != null ) {
        ex = svc.getExecutor(executorName);
    }
    String protocolName = attributes.getValue("protocol");
    Connector con = new Connector(protocolName);
    if (ex != null) {
        setExecutor(con, ex);
    }
    String sslImplementationName = attributes.getValue("sslImplementationName");
    if (sslImplementationName != null) {
        setSSLImplementationName(con, sslImplementationName);
    }
    digester.push(con);

    StringBuilder code = digester.getGeneratedCode();
    if (code != null) {
        code.append(System.lineSeparator());
        code.append(Connector.class.getName()).append(' ').append(digester.toVariableName(con));
        code.append(" = new ").append(Connector.class.getName());
        code.append("(new ").append(con.getProtocolHandlerClassName()).append("());");
        code.append(System.lineSeparator());
        if (ex != null) {
            code.append(digester.toVariableName(con)).append(".getProtocolHandler().setExecutor(");
            code.append(digester.toVariableName(svc)).append(".getExecutor(").append(executorName);
            code.append("));");
            code.append(System.lineSeparator());
        }
        if (sslImplementationName != null) {
            code.append("((").append(AbstractHttp11JsseProtocol.class.getName()).append("<?>) ");
            code.append(digester.toVariableName(con)).append(".getProtocolHandler()).setSslImplementationName(\"");
            code.append(sslImplementationName).append("\");");
            code.append(System.lineSeparator());
        }
    }

其内调用了Connector的构造器,传递的参数为属性protocol,在server.xml中的Connector:

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

Connector构造器

org.apache.catalina.connector.Connector

public Connector(String protocol) {
    boolean apr = AprStatus.isAprAvailable() &&
        AprStatus.getUseAprConnector();
    ProtocolHandler p = null;
    try {
        p = ProtocolHandler.create(protocol, apr);
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    }
    if (p != null) {
        protocolHandler = p;
        protocolHandlerClassName = protocolHandler.getClass().getName();
    } else {
        protocolHandler = null;
        protocolHandlerClassName = protocol;
    }
    // Default for Connector depends on this system property
    setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}

ProtocolHandler.create(protocol, apr)根据protocol参数的不同创建不同的ProtocolHandler实例,以HTTP/1.1为例,由于默认情况下Apr不可用,所以protocolHandlerClassName会被设置为org.apache.coyote.http11.Http11NioProtocol,那么反射生成的protocolHandler就是Http11Protocol实例,Tomcat默认还会配置协议的是AJP/1.3的Connector,那么此Connector的protocolHandler就是org.apache.coyote.ajp.AjpProtocol

org.apache.coyote.ProtocolHandler.create()

public static ProtocolHandler create(String protocol, boolean apr)
        throws ClassNotFoundException, InstantiationException, IllegalAccessException,
        IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
    if (protocol == null || "HTTP/1.1".equals(protocol)
            || (!apr && org.apache.coyote.http11.Http11NioProtocol.class.getName().equals(protocol))
            || (apr && org.apache.coyote.http11.Http11AprProtocol.class.getName().equals(protocol))) {
        if (apr) {
            return new org.apache.coyote.http11.Http11AprProtocol();
        } else {
            return new org.apache.coyote.http11.Http11NioProtocol();
        }
    } else if ("AJP/1.3".equals(protocol)
            || (!apr && org.apache.coyote.ajp.AjpNioProtocol.class.getName().equals(protocol))
            || (apr && org.apache.coyote.ajp.AjpAprProtocol.class.getName().equals(protocol))) {
        if (apr) {
            return new org.apache.coyote.ajp.AjpAprProtocol();
        } else {
            return new org.apache.coyote.ajp.AjpNioProtocol();
        }
    } else {
        // Instantiate protocol handler
        Class<?> clazz = Class.forName(protocol);
        return (ProtocolHandler) clazz.getConstructor().newInstance();
    }
}

除此之外,ProtocolHandler还有其它实现:

在这里插入图片描述

将ProtocolHandler、MapperListener注册到JMX

BIO Http Connector的ProtocolHandler(即Http11Protocol)的JMX注册名为Catalina:type=ProtocolHandler,prot=8080。BIO Http Connector的MapperListener的注册名为Catalina:type=Mapper,prot=8080。AJP Connector的ProtocolHandler(即AjpProtocol)的JMX注册名为Catalinatype=ProtocolHandler,prot=8009,AJP Connector的MapperListener的注册名为Catalina:type=Mapper,port=8009

注:在旧版本中AJP的配置在server.xml中,而新版本(Tomcat9)则在`org.apache.catalina.ha.backend.HeartbeatListener内固定配置8009端口

Connector的启动

根据Tomcat的生命周期管理得知,Tomcat中有很多的容器。Server、Service、Connector这三个容器的初始化顺序为:Server→Service→Connector。在Tomcat 9中:ProtocolHandler的初始化在Connector的initInternal方法中调用,MapperListener的初始化则在StandardService的初始化方法中调用。

**而在旧版本Tomcat中:**ProtocolHandler的初始化稍微有点特殊,ProtocolHandler作为Connector的子容器,其初始化过程并不是由Connector的initInternal方法调用的,而是与启动过程中一道被Connector的startInternal方法所调用,Connector.startInternal()内会初始化ProtocolHandler,启动ProtocolHandler,初始化MapperListener。

org.apache.catalina.connector.Connector.startInternal()

/**
 * Begin processing requests via this Connector.
 *
 * @exception LifecycleException if a fatal startup error occurs
 */
@Override
protected void startInternal() throws LifecycleException {

    // Validate settings before starting
    String id = (protocolHandler != null) ? protocolHandler.getId() : null;
    if (id == null && getPortWithOffset() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
    }

    setState(LifecycleState.STARTING);

    try {
        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

初始化ProtocolHandler

从ProtocolHandler类继承层级克制,ProtocolHandler的子类都必须实现AbstractProtocol抽象类,而protocolHandler.init()的逻辑代码正式在这个抽象类里面

org.apache.coyote.AbstractProtocol.init()

@Override
public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
        logPortOffset();
    }

    if (oname == null) {
        // Component not pre-registered so register it
        oname = createObjectName();
        if (oname != null) {
            Registry.getRegistry(null, null).registerComponent(this, oname, null);
        }
    }

    if (this.domain != null) {
        ObjectName rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        this.rgOname = rgOname;
        Registry.getRegistry(null, null).registerComponent(
                getHandler().getGlobal(), rgOname, null);
    }

    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);

    endpoint.init();
}
AbstractProtocol的初始化步骤:
设置AbstractEndPoint

org.apache.coyote.AbstractProtocol的构造

public AbstractProtocol(AbstractEndpoint<S,?> endpoint) {
    this.endpoint = endpoint;
    setConnectionLinger(Constants.DEFAULT_CONNECTION_LINGER);
    setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
}
设置AbstractProtocolHandler

AbstractAjpProtocol 继承自 AbstractProtocol

org.apache.coyote.ajp.AbstractAjpProtocol的构造

public AbstractAjpProtocol(AbstractEndpoint<S,?> endpoint) {
    super(endpoint);
    setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
    // AJP does not use Send File
    getEndpoint().setUseSendfile(false);
    // AJP listens on loopback by default
    getEndpoint().setAddress(InetAddress.getLoopbackAddress());
    ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
    setHandler(cHandler);
    getEndpoint().setHandler(cHandler);
}
配置socket信息

org.apache.tomcat.util.net.AbstractEndpoint.init()

public abstract void bind() throws Exception;
public abstract void unbind() throws Exception;
public abstract void startInternal() throws Exception;
public abstract void stopInternal() throws Exception;

public final void init() throws Exception {
    if (bindOnInit) {
        bindWithCleanup();
        bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
        // Register endpoint (as ThreadPool - historical name)
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        ObjectName socketPropertiesOname = new ObjectName(domain +
                ":type=SocketProperties,name=\"" + getName() + "\"");
        socketProperties.setObjectName(socketPropertiesOname);
        Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}

其内bindWithCleanup方法调用了本类的bind方法,bind方法内的initServerSocket方法内将ServerSocket绑定到指定的端口

然后AbstractEndpoint以Catalina:type=ThreadPool,name=http-8080注册到JMX,cHandler.global(ConnectorHandler的对象属性,类型为ReuqestGroupInfo)以Catalina:type=SocketProperties,name=http-8080注册到JMX

org.apache.tomcat.util.net.NioEndpoint.bind()

/**
 * Initialize the endpoint.
 */
@Override
public void bind() throws Exception {
    //  将serverSocket绑定到指定端口
    initServerSocket();

    setStopLatch(new CountDownLatch(1));

    // Initialize SSL if needed
    initialiseSsl();

    selectorPool.open(getName());
}

org.apache.tomcat.util.net.NioEndpoint.initServerSocket()

protected void initServerSocket() throws Exception {
    if (getUseInheritedChannel()) {
        // Retrieve the channel provided by the OS
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) {
            serverSock = (ServerSocketChannel) ic;
        }
        if (serverSock == null) {
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        }
    } else if (getUnixDomainSocketPath() != null) {
        SocketAddress sa = JreCompat.getInstance().getUnixDomainSocketAddress(getUnixDomainSocketPath());
        serverSock = JreCompat.getInstance().openUnixDomainServerSocketChannel();
        serverSock.bind(sa, getAcceptCount());
        if (getUnixDomainSocketPathPermissions() != null) {
            Path path = Paths.get(getUnixDomainSocketPath());
            Set<PosixFilePermission> permissions =
                    PosixFilePermissions.fromString(getUnixDomainSocketPathPermissions());
            if (path.getFileSystem().supportedFileAttributeViews().contains("posix")) {
                FileAttribute<Set<PosixFilePermission>> attrs = PosixFilePermissions.asFileAttribute(permissions);
                Files.setAttribute(path, attrs.name(), attrs.value());
            } else {
                java.io.File file = path.toFile();
                if (permissions.contains(PosixFilePermission.OTHERS_READ) && !file.setReadable(true, false)) {
                    log.warn(sm.getString("endpoint.nio.perms.readFail", file.getPath()));
                }
                if (permissions.contains(PosixFilePermission.OTHERS_WRITE) && !file.setWritable(true, false)) {
                    log.warn(sm.getString("endpoint.nio.perms.writeFail", file.getPath()));
                }
            }
        }
    } else {
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
        serverSock.bind(addr, getAcceptCount());
    }
    serverSock.configureBlocking(true); //mimic APR behavior
}
启动ProtocolHandler

启动ProtocolHandler调用的方法是AbstractProtocol.start()

org.apache.coyote.AbstractProtocol.start()

@Override
public void start() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
        logPortOffset();
    }
    //  1.调用Endpoint.start()
    endpoint.start();
    //  2.开启异步超时线程,线程执行单元为AsyncTimeout
    monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
            () -> {
                if (!isPaused()) {
                    startAsyncTimeout();
                }
            }, 0, 60, TimeUnit.SECONDS);
}

从中可知AbstractProtocol.start()方法完成了:

  1. 调用Endpoint.start()

  2. 开启异步超时线程,线程执行单元为AsyncTimeout

org.apache.tomcat.util.net.AbstractEndpoint.start()

public final void start() throws Exception {
    if (bindState == BindState.UNBOUND) {
        bindWithCleanup();
        bindState = BindState.BOUND_ON_START;
    }
    startInternal();
}

org.apache.tomcat.util.net.NioEndpoint…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() + "-ClientPoller");
        pollerThread.setPriority(threadPriority);
        pollerThread.setDaemon(true);
        pollerThread.start();

        startAcceptorThread();
    }
}

protected void startAcceptorThread() {
    acceptor = new Acceptor<>(this);
    String threadName = getName() + "-Acceptor";
    acceptor.setThreadName(threadName);
    Thread t = new Thread(acceptor, threadName);
    t.setPriority(getAcceptorThreadPriority());
    t.setDaemon(getDaemon());
    t.start();
}

Endpoint.start()中主要完成了:

  1. 确保ServerSocket已经绑定端口
  2. 创建工作者线程池
  3. 初始化连接latch,用于限制请求的并发量
  4. 开启poller线程。poller用于接收线程生产的消息(或事件)进行处理,poller最终调用的是Handler的代码
  5. 开启acceptor线程
启动MapperListener

org.apache.catalina.mapper.MapperListener.startInternal()

@Override
public void startInternal() throws LifecycleException {

    setState(LifecycleState.STARTING);

    Engine engine = service.getContainer();
    if (engine == null) {
        return;
    }

    findDefaultHost();

    addListeners(engine);

    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            // Registering the host will register the context and wrappers
            registerHost(host);
        }
    }
}
MapperListener的启动步骤
查找默认Host

StandardService的子容器包括:StandardEngineConnectorExecutor。MapperListener本身会持有Connector,所有可以通过各个容器的父子关系,找到Connector的同级容器StandardEngine。StandardHost是StandardEngine的子容器,Engine和Host的默认配置如下:

tomcat/conf/server.xml

<Engine name="Catalina" defaultHost="localhost">

  <Realm className="org.apache.catalina.realm.LockOutRealm">

    <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
           resourceName="UserDatabase"/>
  </Realm>

  <Host name="localhost"  appBase="webapps"
        unpackWARs="true" autoDeploy="true">
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="localhost_access_log" suffix=".txt"
           pattern="%h %l %u %t &quot;%r&quot; %s %b" />
  </Host>
</Engine>

MapperListener.startInternal()中的findDefault方法可以获取上面配置中的默认Host,Engine元素的defaultHost属性值必须要与配置的某个Host元素的name属性值相同。如果defaultHost的属性值配置无误,则会添加为MapperListener的Mappler对象属性的defaultHostName

org.apache.catalina.mapper.MapperListener.findDefaultHost()

private void findDefaultHost() {

    Engine engine = service.getContainer();
    String defaultHost = engine.getDefaultHost();

    boolean found = false;

    if (defaultHost != null && defaultHost.length() > 0) {
        Container[] containers = engine.findChildren();

        for (Container container : containers) {
            Host host = (Host) container;
            if (defaultHost.equalsIgnoreCase(host.getName())) {
                found = true;
                break;
            }

            String[] aliases = host.findAliases();
            for (String alias : aliases) {
                if (defaultHost.equalsIgnoreCase(alias)) {
                    found = true;
                    break;
                }
            }
        }
    }

    if (found) {
        mapper.setDefaultHostName(defaultHost);
    } else {
        log.error(sm.getString("mapperListener.unknownDefaultHost", defaultHost, service));
    }
}
将Host及其子容器Context,Context的子容器Wrapper注册到MapperListener的Mapper对象中

Mapper的数据结构

org.apache.catalina.mapper.Mapper

/**
     * Array containing the virtual hosts definitions.
     */
    protected Host[] hosts = new Host[0];


    /**
     * Default host name.
     */
    protected String defaultHostName = null;

    /**
     * Context associated with this wrapper, used for wrapper mapping.
     */
    protected Context context = new Context();

    protected static abstract class MapElement {

        public String name = null;
        public Object object = null;

    }

    protected static final class Host
        extends MapElement {

        public ContextList contextList = null;

    }

    protected static final class ContextList {

        public Context[] contexts = new Context[0];
        public int nesting = 0;

    }

    protected static final class Context
        extends MapElement {

        public String path = null;
        public String[] welcomeResources = new String[0];
        public javax.naming.Context resources = null;
        public Wrapper defaultWrapper = null;
        public Wrapper[] exactWrappers = new Wrapper[0];
        public Wrapper[] wildcardWrappers = new Wrapper[0];
        public Wrapper[] extensionWrappers = new Wrapper[0];
        public int nesting = 0;

    }

    protected static class Wrapper
        extends MapElement {

        public String path = null;
        public boolean jspWildCard = false;
    }

从中可知,Mapper中维护着一个Host数组,每个Host中油一个ContextList,这个ContextList中维护着一个Context数据。每个Context维护着一个defaultWrapper,三个Wrapper数组(exactWrappers、wildcardWrappers、extensionWrappers)

  • Host:代表一个虚拟主句,各Host的name不能相同,appBase代表各虚拟主机的应用发布位置
  • Context:代表一个应用,Context可以根据应用的/WEB-INF/web.xml文件中定义的servelet来处理请求。一个Host下可以有多个Context
  • Wrapper:代表一个Servlet或者jsp,它负责管理一个Servlet,包括Servlet的装载、初始化、执行以及资源回收

注:在Tomcat 9中MapperListener的初始化在StandardService初始化的时候交由LifecycleMBeanBase实现,在旧版本中MapperListener大多数初始化时执行的流程将在启动的时候完成

到目前为止,Tomcat处理请求前作的初始化和准备工作已经完成

请求处理架构

请求处理架构图

在这里插入图片描述

  • Accept:负责从ServerSocket中接收新的连接,并将Socket转交给SocketProcessor处理。Acceptor线程的默认大小为1,可以在server.xml的Connector配置中增加acceptorThreadCount的大小,但是该属性将在Tomcat10中移除

  • SocketProcessor:负责对Acceptor转交的Socket进行处理,包括给Socket设置属性、读取请求行和请求头等,最终将处理交给Engine的Pipline处理

  • ThreadPool:执行SocketProcessor的线程是在Endpoint.start()中设置的线程池,次线程池默认的最小线程数minSpareThreads等于10,最大线程数maxThreads等于200,介意在server.xml的Connector配置中调整它们的大小

  • Pipeline:SocketProcessor线程最后会将请求进一步交给Engine容器的Pipeline,管道Pipeline包括一些列的value,如:StandardEngineValve、AccessLogValve、ErrorResportValve、StandardHostValve、StandardContextValve、StandardWrapperValve,它们就像地下水管中的一个个阀门,每一个都会对请求数据做不同的处理

  • FilterChain:管道Pipeline的最后一个valve是StandardWrapper,它会负责生成Servlet和Filter实例,并将它们组织成对请求处理的链条,这里正是Tomcat与J2EE规范相结合的部分

默认情况下,Tomcat只有一个Acceptor线程,Acceptor不断循环从ServerSocket中获取Socket,Acceptor的实现非常轻量级,它只负责两个动作:获取Socket和将Socket转交给SocketProcessor线程处理。另外,在server.xml的Connector配置中添加acceptorThreadCount的值,让我们同时可以拥有多个Acceptor线程。虽然可以修改maxThreads配置把SocketProcessor的线程数设置很大,但是需要因地制宜:

  • 如果部署在Tomcat上的Web服务主要用于计算,那么CPU的开销势必会很大,那么线程数不宜设置的过大,一般以CPU核数 * 2 —— CPU核数 * 3最佳。如果计算量非常大,就已经超过Tomcat的使用范畴,此时应该选择Hadoop或者Storm、Spark才是更好的选择

  • 如果不熟在Tomcat上的Web服务主要是为了提供数据库访问,此时I/O的开销会很大,而CPU利用率反而很低,此时应该将线程数设置的大一些,但是如果设置的过大,CPU为了给成百上千线程分配时间片,造成CPU的精灵粉扫在线程切换上,反而造成性能下降,具体数值还需根据系统性能调优得出

注:在Tomcat10中server.xml的Connector中acceptorThreadCount属性将被移除

接收请求

Acceptor实现了Runnable接口。Acceptor作为后台线程不断循环,每次循环都会sleep大约1s(由于是线程级别的,所以并不保证准确),然后接收来自浏览器的Socket连接(用户在浏览器输入HTTP请求地址后,浏览器底层实际使用Socket通信的),最后将Socket交给Endpoint的processSocket方法处理

Acceptor

从Connector整体结构图里面可知请求的入口是在Acceptor。Endpoint.start()方法会开启Acceptor线程来处理请求,因此接下来分析Acceptor线程中的执行逻辑

org.apache.tomcat.util.net.Acceptor.run()

@Override
public void run() {

    int errorDelay = 0;

    try {
        // Loop until we receive a shutdown command
        while (!stopCalled) {

            // Loop if endpoint is paused
            // 1.运行过程中,如果Endpoint暂停了,则Acceptor进行自旋(间隔50ms)
            while (endpoint.isPaused() && !stopCalled) {
                state = AcceptorState.PAUSED;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
            // 2.如果Endpoint终止运行了,则Acceptor也会停止
            if (stopCalled) {
                break;
            }
            state = AcceptorState.RUNNING;

            try {
                // if we have reached max connections, wait
                // 3.如果请求达到了最大连接数,则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
                    // 4.接收下一次连接的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
                    // 5.setSocketOptions()这里是关键,会将socket以事件的方式传递给poller
                    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;
}

Acceptor.run()方法会做下面几件事情

  1. 运行过程中,如果Endpoint暂停了,则Acceptor进行自旋(间隔50ms)
  2. 如果Endpoint终止运行了,则Acceptor也会终止
  3. 如果请求达到了最大连接数,则wait直到连接数降下来
  4. 接收下一次连接的socket
  5. setSocketOptions方法是关键,会将socket以事件的方式传递给poller

org.apache.tomcat.util.net.NioEndpoint.setSocketOptions()

/**
 * Process the specified connection.
 * @param socket The socket channel
 * @return <code>true</code> if the socket was correctly configured
 *  and processing may continue, <code>false</code> if the socket needs to be
 *  close immediately
 */
@Override
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, selectorPool, 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());

        // 将channel注册到poller
        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;
}

setSocketOptions()将channel注册到Poller

org.apache.tomcat.util.net.NioEndpoint.register()

org.apache.tomcat.util.net.NioEndpoint.addEvent()

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);
}

private void addEvent(PollerEvent event) {
    events.offer(event);
    if (wakeupCounter.incrementAndGet() == 0) {
        selector.wakeup();
    }
}

因为Poller维持了 一个events同步队列,所以Acceptor接收到的channel会放在这个队列里面,放置的方法为addEvent()

Poller

Acceptor生成了事件PollerEvent,那么Poller必然会对这些事件进行消费,接下来分析Poller.run()。真正处理key的地方在于processKey(sk, socketWrapper);

org.apache.tomcat.util.net.NioEndpoint.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) {
                    // 真正处理key的地方
                    processKey(sk, socketWrapper);
                }
            }
    
            // Process timeouts
            timeout(keyCount,hasEvents);
        }
    
        getStopLatch().countDown();
    } 
}

processKey()内根据key的类型,分别处理读和写,用来处理的方法是processSocket(),其内主要完成了:

  1. 处理读事件,比如生成Request对象
  2. 处理写事件,比如讲生成的Response对象通过socket写回客户端

org.apache.tomcat.util.net.NioEndpoint.Poller.processKey()

protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
    try {
        if (close) {
            cancelledKey(sk, socketWrapper);
        } else if (sk.isValid() && socketWrapper != null) {
            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
                    // 1.处理读事件,比如生成Request对象
                    if (sk.isReadable()) {
                        if (socketWrapper.readOperation != null) {
                            if (!socketWrapper.readOperation.process()) {
                                closeSocket = true;
                            }
                        } else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
                            closeSocket = true;
                        }
                    }
                    // 2.处理写事件,比如将生成的Response对象通过socket写回客户端
                    if (!closeSocket && sk.isWritable()) {
                        if (socketWrapper.writeOperation != null) {
                            if (!socketWrapper.writeOperation.process()) {
                                closeSocket = true;
                            }
                        } 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);
    }
}

processSocket方法先是从processorCache里面那一个Processor来处理socket,Processor的实现为SocketProcessor,然后将Processor放到工作线程池中执行

org.apache.tomcat.util.net.AbstractEndpoint.processSocket()

public boolean processSocket(SocketWrapperBase<S> socketWrapper,
        SocketEvent event, boolean dispatch) {
    try {
        if (socketWrapper == null) {
            return false;
        }
        // 1.从processorCache里面拿一个Processor来处理socket,Processor的实现视为SocketProcessor
        SocketProcessorBase<S> sc = null;
        if (processorCache != null) {
            sc = processorCache.pop();
        }
        if (sc == null) {
            sc = createSocketProcessor(socketWrapper, event);
        } else {
            sc.reset(socketWrapper, event);
        }
        // 2.将Processor放到工作线程池中执行
        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;
}

SocketProcessor.run()内部将调用SocketProcessor.doRun(),该方法将处理逻辑交给Handler处理,当event为null时,则表明是一个OPEN_READ事件

org.apache.tomcat.util.net.NioEndpint.SocketProcessor

/**
 * This class is the equivalent of the Worker, but will simply use in an
 * external Executor thread pool.
 */
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {

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

    @Override
    protected void doRun() {
        
        Poller poller = NioEndpoint.this.poller;
        if (poller == null) {
            socketWrapper.close();
            return;
        }

        try {
            int handshake = -1;
            try {
                if (socketWrapper.getSocket().isHandshakeComplete()) {
                    
                    handshake = -1;
                } else {
                    handshake = socketWrapper.getSocket().handshake(event == SocketEvent.OPEN_READ, event == SocketEvent.OPEN_WRITE);
                    
                    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
                // 将处理逻辑交给Handler处理,当事件为null时,则表明是一个OPEN_READ事件
                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 && !paused && processorCache != null) {
                processorCache.push(this);
            }
        }
    }

    private SelectionKey getSelectionKey() {
        SocketChannel socketChannel = socketWrapper.getSocket().getIOChannel();
        if (socketChannel == null) {
            return null;
        }

        return socketChannel.keyFor(NioEndpoint.this.poller.getSelector());
    }
}

Handler的实现 —— ConnectionHandler

Handler的关键方法是process(),该方法非常的长,超过了200行!,虽然这个方法有很多条件分支,但是逻辑缺非常清楚,主要是调用Processor.process()

org.apache.coyote.AbstractProtocol.ConnectionHandler.process()

@Override
public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
    if (getLog().isDebugEnabled()) {
        getLog().debug(sm.getString("abstractConnectionHandler.process",
                wrapper.getSocket(), status));
    }
    if (wrapper == null) {
        // Nothing to do. Socket has been closed.
        return SocketState.CLOSED;
    }

    S socket = wrapper.getSocket();

    Processor processor = (Processor) wrapper.getCurrentProcessor();
    if (getLog().isDebugEnabled()) {
        getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet",
                processor, socket));
    }

    if (SocketEvent.TIMEOUT == status &&
            (processor == null ||
            !processor.isAsync() && !processor.isUpgrade() ||
            processor.isAsync() && !processor.checkAsyncTimeoutGeneration())) {
        // This is effectively a NO-OP
        return SocketState.OPEN;
    }

    if (processor != null) {
        // Make sure an async timeout doesn't fire
        getProtocol().removeWaitingProcessor(processor);
    } else if (status == SocketEvent.DISCONNECT || status == SocketEvent.ERROR) {
        // Nothing to do. Endpoint requested a close and there is no
        // longer a processor associated with this socket.
        return SocketState.CLOSED;
    }

    ContainerThreadMarker.set();

    try {
        if (processor == null) {
            String negotiatedProtocol = wrapper.getNegotiatedProtocol();
            // OpenSSL typically returns null whereas JSSE typically
            // returns "" when no protocol is negotiated
            if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) {
                UpgradeProtocol upgradeProtocol = getProtocol().getNegotiatedProtocol(negotiatedProtocol);
                if (upgradeProtocol != null) {
                    processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
                    if (getLog().isDebugEnabled()) {
                        getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
                    }
                } else if (negotiatedProtocol.equals("http/1.1")) {
                    // Explicitly negotiated the default protocol.
                    // Obtain a processor below.
                } else {
                    if (getLog().isDebugEnabled()) {
                        getLog().debug(sm.getString("abstractConnectionHandler.negotiatedProcessor.fail",
                                negotiatedProtocol));
                    }
                    return SocketState.CLOSED;
                    /*
                     * To replace the code above once OpenSSL 1.1.0 is
                     * used.
                    // Failed to create processor. This is a bug.
                    throw new IllegalStateException(sm.getString(
                            "abstractConnectionHandler.negotiatedProcessor.fail",
                            negotiatedProtocol));
                    */
                }
            }
        }
        if (processor == null) {
            processor = recycledProcessors.pop();
            if (getLog().isDebugEnabled()) {
                getLog().debug(sm.getString("abstractConnectionHandler.processorPop", processor));
            }
        }
        if (processor == null) {
            processor = getProtocol().createProcessor();
            register(processor);
            if (getLog().isDebugEnabled()) {
                getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
            }
        }

        processor.setSslSupport(
                wrapper.getSslSupport(getProtocol().getClientCertProvider()));

        // Associate the processor with the connection
        wrapper.setCurrentProcessor(processor);

        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
                        wrapper.setCurrentProcessor(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
                    wrapper.setCurrentProcessor(processor);
                    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);
                        }
                    }
                    if (httpUpgradeHandler instanceof InternalHttpUpgradeHandler) {
                        if (((InternalHttpUpgradeHandler) httpUpgradeHandler).hasAsyncIO()) {
                            // The handler will initiate all further I/O
                            state = SocketState.UPGRADED;
                        }
                    }
                }
            }
        } while ( state == SocketState.UPGRADING);

        if (state == SocketState.LONG) {
            longPoll(wrapper, processor);
            if (processor.isAsync()) {
                getProtocol().addWaitingProcessor(processor);
            }
        } else if (state == SocketState.OPEN) {
            // In keep-alive but between requests. OK to recycle
            // processor. Continue to poll for the next request.
            wrapper.setCurrentProcessor(null);
            release(processor);
            wrapper.registerReadInterest();
        } else if (state == SocketState.SENDFILE) {
        } else if (state == SocketState.UPGRADED) {
            if (status != SocketEvent.OPEN_WRITE) {
                longPoll(wrapper, processor);
                getProtocol().addWaitingProcessor(processor);
            }
        } else if (state == SocketState.SUSPENDED) {
            
        } else {
            // Connection closed. OK to recycle the processor.
            // Processors handling upgrades require additional clean-up
            // before release.
            wrapper.setCurrentProcessor(null);
            if (processor.isUpgrade()) {
                UpgradeToken upgradeToken = processor.getUpgradeToken();
                HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
                InstanceManager instanceManager = upgradeToken.getInstanceManager();
                if (instanceManager == null) {
                    httpUpgradeHandler.destroy();
                } else {
                    ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null);
                    try {
                        httpUpgradeHandler.destroy();
                    } finally {
                        try {
                            instanceManager.destroyInstance(httpUpgradeHandler);
                        } catch (Throwable e) {
                            ExceptionUtils.handleThrowable(e);
                            getLog().error(sm.getString("abstractConnectionHandler.error"), e);
                        }
                        upgradeToken.getContextBind().unbind(false, oldCL);
                    }
                }
            }
            release(processor);
        }
        return state;
    } catch(java.net.SocketException e) {
        // 省略异常处理无关代码
    } catch (java.io.IOException e) {
        // 省略异常处理无关代码
    } catch (ProtocolException e) {
        // 省略异常处理无关代码
    }
    // above.
    catch (OutOfMemoryError oome) {
        getLog().error(sm.getString("abstractConnectionHandler.oome"), oome);
    } catch (Throwable e) {
        ExceptionUtils.handleThrowable(e);
        getLog().error(sm.getString("abstractConnectionHandler.error"), e);
    } finally {
        ContainerThreadMarker.clear();
    }

    // Make sure socket/processor is removed from the list of current
    // connections
    wrapper.setCurrentProcessor(null);
    release(processor);
    return SocketState.CLOSED;
}

其中的关键是94行的process方法,在process方法内调用了service(),该方法也非常的长,若想了解自行翻找源码,service方法内生成了Request和Response对象,然后调用Adapter.service(),将生成的Request和Response对象传进去。

org.apache.coyote.AbstractProcessorLight.process()

@Override
public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
        throws IOException {

    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) {
            // 调用service方法
            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;
}

接下来便是分析Adapter

Adapter

Adapter用于连接Connector和Container,起到承上启下的作用。Processor会调用Adapter.service(),该方法内主要完成了以下几件事情:

  1. 根据coyote框架的request和response对象,生成connector的request和response对象(即HttpServletRequest和HttpServletResponse的封装)
  2. 补充header
  3. 解析请求,该方法会出现代理服务器、设置必要的header等操作
  4. 真正进入容器的地方,调用Engine容器下pipeline的阀门

org.apache.catalina.connector.CoyoteAdapter.service()

@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
        throws Exception {
    
    // 1.根据coyote框架的request和response对象,生成connector的request和response对象(是HttpServletRequest和HttpServletResponse的封装)
    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());
    }

    // 2.补充header
    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
        // 3.解析请求,该方法会出现代理服务器、设置必要的header等操作
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            //check valves if we support async
            request.setAsyncSupported(
                    connector.getService().getContainer().getPipeline().isAsyncSupported());
            // Calling the container
            // 4.真正进入容器的地方,调用Engine容器下pipeline的阀门
            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 (!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()) {
            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();
            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();
        }
    }
}

其内postParseRequest方法完成以下流程:

  1. 解析请求url中的参数
  2. URI decoding的转换
  3. 调用normalize方法判断请求路径中是否存在 “”, “//”, “/./” 和 “/…/” ,如果存在则处理结束
  4. 调用convertURI方法将字节转换为字符
  5. 调用checkNormalize方法判断uri是否存在 “”, “//”, “/./” 和 “/…/” ,如果存在则处理结束
  6. 调用COnnector的getMapper方法获取Mapper,然后调用Mapper的map方法对host和context进行匹配(比如http://localhost:8080/manager/status会匹配host: localhost, context: /manager),其实质是调用internalMap方法
  7. 使用ApplicationSessionCookieConfig.getSessionUriParamName获取sessionid的key,然后获取sessionid
  8. 调用parseSessionCookiesId和parseSessionSslId方法查找cookie或者SSL中的sessionid

org.apache.catalina.mapper.Mapper.map()

public void map(MessageBytes host, MessageBytes uri, String version,
                MappingData mappingData) throws IOException {

    if (host.isNull()) {
        String defaultHostName = this.defaultHostName;
        if (defaultHostName == null) {
            return;
        }
        host.getCharChunk().append(defaultHostName);
    }
    host.toChars();
    uri.toChars();
    internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
}

在CoyoteAdapter的service方法最后会将请求交给Engine的Pipeline处理

Pipline

在Tomcat中管道Pipeline是一个接口,定义使得一组阀门Valve按照顺序执行的规范,Pipeline中定义的接口如下:

  • getBasic:获取管道的基础阀门
  • setBasic:设置管道的基础阀门
  • addValve:添加阀门
  • getValves:获取阀门集合
  • removeValve:移除阀门
  • getFirst:获取第一个阀门
  • isAsyncSupported:当管道中的所有阀门都支持异步时返回true,否则返回false
  • getContainer:获取管道相关的容器,比如StandardEngine
  • setContainer:设置管道相关联的容器

Engine、Host、Context及Wrapper等容器都定义了自身的Pipline,每个Pipline都包含一到多个Valve。Valve定义了各个阀门的接口规范

在这里插入图片描述

  • Valve:定义了管道中阀门的接口规范,getNext和setNext分别用于获取或者设置当前阀门的下游阀门,invoke方法用来应用当前阀门的操作
  • ValveBase:Valve接口的基本实现,ValveBase与Valve的具体实现采用抽象模板模式将管道中的阀门串联起来
  • StandardEngineValve:StandardEngine中的唯一阀门,主要用于从request中选择其host映射的Host容器StandardHost
  • AccessLogValve:StandardHost中的第一个饭呢,主要用于管道执行结束之后记录日志信息
  • ErrorReportValve:StandardHost中紧跟AccessLogValve的阀门,主要用于管道执行结束后,从request对象中获取异常信息,并封装到response中以便将问题展现给访问者
  • StandardHostValve:StandardContext中的唯一阀门,主要作用是禁止任何对WEB-INF或META-INF目录下资源的重定向访问,对应用程序热部署功能的实现,从request中获得StandardWrapper
  • StandardWrapperValve:StandardWrapper中的唯一阀门,主要作用包括调用StandardWrapper的loadServlet方法生成Servlet实例和调用ApplicationFilterFactory生成Filter链

在CoyoteAdapter的service方法中可知,执行管道的代码如下

org.apache.catalina.connector.CoyoteAdapter

connector.getService().getContainer().getPipeline().getFirst().invoke(
        request, response);

getContainer方法获取到的实际是StandardService中的StandardEngine容器,从Tomcaat生命周期管理中可知,StandardEngine继承自ContainerBase,所以这里的getPipline方法实际是ContainerBase实现的

org.apache.catalina.core.ContainerBase.getPipline()

@Override
public Pipeline getPipeline() {
    return this.pipeline;
}

pipeline则在ContainerBase实例化时生成,StandardPipline是Pipline的标准实现

protected final Pipeline pipeline = new StandardPipeline(this);

随后调用了StandardPipline的getFirst方法来获取管道中的第一个Valve,由于TOmcat并没有为StandardEngine的StandardPipline设置first,因此将返回StandardPipline的basic即valve

org.apache.catalina.core.StandardPipline.getFirst()

@Override
public Valve getFirst() {
    if (first != null) {
        return first;
    }

    return basic;
}

其内basic的类型是StandardEngineValve,StandardEngineValve的加载是在server.xml文件的加载与解析中ObjectCreateRule执行的begin方法,begin方法会反射调用StandardEngine构造器生成StandardEngine的实例,StandardEngine的构造器中就会给其StandardPipeline设置basic为StandardEngineValve

org.apache.catalina.core.StandardEngine.StandardEngine()

/**
 * Create a new StandardEngine component with the default basic Valve.
 */
public StandardEngine() {

    super();
    pipeline.setBasic(new StandardEngineValve());
    /* Set the jmvRoute using the system property jvmRoute */
    try {
        setJvmRoute(System.getProperty("jvmRoute"));
    } catch(Exception ex) {
        log.warn(sm.getString("standardEngine.jvmRouteFail"));
    }
    // By default, the engine will hold the reloading thread
    backgroundProcessorDelay = 10;

}

connector在最后调用了StandardEngineValve的invoke方法,正式将请求交给管道处理。根据Adapter.postParseRequest()介绍,request已经被映射到相对应的Context容器(比如/manager)。此处首先调用request的getHost方法(实质是通过request映射的Context容器获取父容器得到)获取Host容器,然后调用Host容器的Pipeline的getFirst方法获得AccessLogValve。congAccessLogValve的invoke方法中可以看出调用了getNext方法获取Host容器的Pipeline的下一个Valve,并调用其invoke方法

org.apache.catalina.core.StandardEngineValve.invoke()

@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);
}

org.apache.catalina.connector.Request.getHost()

/**
 * @return the Host within which this Request is being processed.
 */
public Host getHost() {
    return mappingData.host;
    // return (Host) getContext().getParent();
}

org.apache.catalina.valves.AbstractAccessLogValve.invoke()

@Override
public void invoke(Request request, Response response) throws IOException,
        ServletException {
    if (tlsAttributeRequired) {
        // The log pattern uses TLS attributes. Ensure these are populated
        // before the request is processed because with NIO2 it is possible
        // for the connection to be closed (and the TLS info lost) before
        // the access log requests the TLS info. Requesting it now causes it
        // to be cached in the request.
        request.getAttribute(Globals.CERTIFICATES_ATTR);
    }
    if (cachedElements != null) {
        for (CachedElement element : cachedElements) {
            element.cache(request);
        }
    }
    getNext().invoke(request, response);
}

根据以上分析可知StandardEngine容器的Pipeline中只有一个Valve(StandardEngineValve),而StandardHost容器中油三个Valve(AccessLogValve、ErrorReportValve和StandardHostValve),此外StandardContext容器中油一个Valve(StandardContextValve),StandardWrapper中也只有一个Valve(StandardWrapperValve)。这些阀门Valve通过invoke方法批次串联起来,最终构成的执行顺序时分类似于一个管道

在这里插入图片描述

目前已StandardEngineValve和AccessLogValve为例讲了Valve的实现,以及Pipline是如何串联起来的,最后再分析StandardWrapperValve的实现,其它Valve的实现不再赘述

Filter与职责链模式

根据对管道和阀门的分析,我们知道要分析StandardWrapperValve,只要阅读其invoke方法即可

org.apache.catalina.core.StandardWrapperValve.invoke()

/**
 * Invoke the servlet we are managing, respecting the rules regarding
 * servlet lifecycle and SingleThreadModel support.
 *
 * @param request Request to be processed
 * @param response Response to be produced
 *
 * @exception IOException if an input/output error occurred
 * @exception ServletException if a servlet error occurred
 */
@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Initialize local variables we may need
    boolean unavailable = false;
    Throwable throwable = null;
    // This should be a Request attribute...
    long t1=System.currentTimeMillis();
    requestCount.incrementAndGet();
    StandardWrapper wrapper = (StandardWrapper) getContainer();
    Servlet servlet = null;
    Context context = (Context) wrapper.getParent();

    // Check for the application being marked unavailable
    if (!context.getState().isAvailable()) {
        response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                       sm.getString("standardContext.isUnavailable"));
        unavailable = true;
    }

    // 省略校验即次要代码

    // Allocate a servlet instance to process this request
    try {
        if (!unavailable) {
            servlet = wrapper.allocate();
        }
    } catch (UnavailableException e) {
        // 省略异常处理代码
    } catch (ServletException e) {
        // 省略异常处理代码
    } catch (Throwable e) {
        // 省略异常处理代码
    }

    MessageBytes requestPathMB = request.getRequestPathMB();
    DispatcherType dispatcherType = DispatcherType.REQUEST;
    if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
    request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
    request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
            requestPathMB);
    // Create the filter chain for this request
    ApplicationFilterChain filterChain =
            ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

    // Call the filter chain for this request
    // NOTE: This also calls the servlet's service() method
    Container container = this.container;
    try {
        if ((servlet != null) && (filterChain != null)) {
            // Swallow output if needed
            if (context.getSwallowOutput()) {
                try {
                    SystemLogHandler.startCapture();
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        filterChain.doFilter(request.getRequest(),
                                response.getResponse());
                    }
                } finally {
                    String log = SystemLogHandler.stopCapture();
                    if (log != null && log.length() > 0) {
                        context.getLogger().info(log);
                    }
                }
            } else {
                if (request.isAsyncDispatching()) {
                    request.getAsyncContextInternal().doInternalDispatch();
                } else {
                    filterChain.doFilter
                        (request.getRequest(), response.getResponse());
                }
            }

        }
    } catch (ClientAbortException | CloseNowException e) {
        // 省略异常处理代码
    } catch (IOException e) {
        // 省略异常处理代码
    } catch (UnavailableException e) {
        // 省略异常处理代码
   } catch (ServletException e) {
        // 省略异常处理代码
    } catch (Throwable e) {
        // 省略异常处理代码
    } finally {
        // Release the filter chain (if any) for this request
        if (filterChain != null) {
            filterChain.release();
        }

        // Deallocate the allocated servlet instance
        try {
            if (servlet != null) {
                wrapper.deallocate(servlet);
            }
        } catch (Throwable e) {
            // 省略异常处理代码
        }

        // If this servlet has been marked permanently unavailable,
        // unload it and release this instance
        try {
            if ((servlet != null) &&
                (wrapper.getAvailable() == Long.MAX_VALUE)) {
                wrapper.unload();
            }
        } catch (Throwable e) {
            // 省略异常处理代码
        }
        long t2=System.currentTimeMillis();

        long time=t2-t1;
        processingTime += time;
        if( time > maxTime) maxTime=time;
        if( time < minTime) minTime=time;
    }
}

StandardWrapperValve的invoke方法执行步骤如下:

  1. 调用StandardWrapper的allocate方法分配org.apache.catalina.servlets.DefaultServelt的实例处理访问,

    包括* .html、* .htm、* .gif、* .jpg、* .jpeg等资源的request,分配org.apache.jasper.servlet.JspServlet的实例处理访问 *.jpg页面的request。这些Servlet实例是在StandardContext启动的时候调用StandardWrapper的load方法用反射生成的,有关StandardContext启动在Tomcat生命周期管理中提及

  2. 确认当前request是否是Comet的,由于默认的DefaultServlet并未实现CometProcessor接口,所以不会再为Comet的请求处理。Comet值得是一种Web应用程序的架构。在这种架构中,客户端程序(通常是浏览器)不需要显式的像服务端发出请求,服务端会在其数据发生变化的时候主动将数据异步的方法给客户端,从而使客户端能够及时的更新用户界面以反映服务器端数据的变化

  3. 给客户端发送确认

  4. 给request对象设置请求类型和请求路径属性

  5. 获取ApplicationFilterFactory(单例模式实现),并调用其createFilterChain方法创建ApplicationFilterChain

  6. 调用ApplicationFilterChain的doFilter方法,执行ApplicationChain中维护的Filter职责链

  7. 调用ApplicationFilterChain的release方法清空对Servlet、Filter的应用

  8. 调用StandardWrapper的deallocate方法释放其分配的Servlet

注:如果接收请求的Servlet实现了SingleThreadModel接口,那么singleThreadMode属性为true,则Tomcat的StandardWrapper中只有一个Servlet实例,否则会创建一个Servlet实例池

创建Filter职责链用到的createFilterChain()

org.apache.catalina.core.ApplicationFilterChain.createFilterChain()

public static ApplicationFilterChain createFilterChain(ServletRequest request,
        Wrapper wrapper, Servlet servlet) {

    // If there is no servlet to execute, return null
    if (servlet == null)
        return null;

    // Create and initialize a filter chain object
    ApplicationFilterChain filterChain = null;
    if (request instanceof Request) {
        Request req = (Request) request;
        if (Globals.IS_SECURITY_ENABLED) {
            // Security: Do not recycle
            filterChain = new ApplicationFilterChain();
        } else {
            filterChain = (ApplicationFilterChain) req.getFilterChain();
            if (filterChain == null) {
                filterChain = new ApplicationFilterChain();
                req.setFilterChain(filterChain);
            }
        }
    } else {
        // Request dispatcher in use
        filterChain = new ApplicationFilterChain();
    }

    filterChain.setServlet(servlet);
    filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

    // Acquire the filter mappings for this Context
    StandardContext context = (StandardContext) wrapper.getParent();
    FilterMap filterMaps[] = context.findFilterMaps();

    // If there are no filter mappings, we are done
    if ((filterMaps == null) || (filterMaps.length == 0))
        return filterChain;

    // Acquire the information we will need to match filter mappings
    DispatcherType dispatcher =
            (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

    String requestPath = null;
    Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
    if (attribute != null){
        requestPath = attribute.toString();
    }

    String servletName = wrapper.getName();

    // Add the relevant path-mapped filters to this filter chain
    for (FilterMap filterMap : filterMaps) {
        if (!matchDispatcher(filterMap, dispatcher)) {
            continue;
        }
        if (!matchFiltersURL(filterMap, requestPath))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMap.getFilterName());
        if (filterConfig == null) {
            // FIXME - log configuration problem
            continue;
        }
        filterChain.addFilter(filterConfig);
    }

    // Add filters that match on servlet name second
    for (FilterMap filterMap : filterMaps) {
        if (!matchDispatcher(filterMap, dispatcher)) {
            continue;
        }
        if (!matchFiltersServlet(filterMap, servletName))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                context.findFilterConfig(filterMap.getFilterName());
        if (filterConfig == null) {
            // FIXME - log configuration problem
            continue;
        }
        filterChain.addFilter(filterConfig);
    }

    // Return the completed filter chain
    return filterChain;
}

整个创建FIlter职责链的过程:

  1. 从request中获取请求的类型(Tomcat目前提供的请求类型有REQUEST、FORWARD、INCLUDE、ASYNC及ERROR五种)与路径

  2. 创建ApplicationFilterChain并设置给当前request

  3. 给ApplicationFilterChain设置Servlet,即DefaultServlet

  4. 从StandardContext中获取当前context的filterMaps

  5. 如果filterMaps为空,则说明当前Context没有配置Filter,否则会将filterMaps中的Filter全部添加到ApplicationFilterChain中的Filter职责链中

调用ApplicationFilterChain的foFilter方法,执行ApplicationFilterChain中维护的Filter职责链。Filter职责链是对职责链模式的经典应用

在这里插入图片描述

其执行过程如下:

  1. StandardWrapperValve.invoke()在创建完ApplicationFilterChain后,第一次调用ApplicationFilterChain.doFilter()

  2. 如果ApplicationChain自身维护的Filter数组中还有没有执行的Filter,则去除此Filter的doFilter(),否则执行Servlet的service()处理请求

  3. 每个Filter首先执行自身的过滤功能,最后再执行结束前会回调ApplicationFilterChain.doFilter()

  4. Servlet的service实际会调用自身的doGet、doHead、doPost、doPut、doDelete等方法

最后从源码实现级别分析Filter职责链的执行过程,首先浏览ApplicationFilterChain.doFilter()

org.apache.catalina.core.ApplicationFilterChain.doFilter()

/**
 * Invoke the next filter in this chain, passing the specified request
 * and response.  If there are no more filters in this chain, invoke
 * the <code>service()</code> method of the servlet itself.
 *
 * @param request The servlet request we are processing
 * @param response The servlet response we are creating
 *
 * @exception IOException if an input/output error occurs
 * @exception ServletException if a servlet exception occurs
 */
@Override
public void doFilter(ServletRequest request, ServletResponse response)
    throws IOException, ServletException {

    if( Globals.IS_SECURITY_ENABLED ) {
        final ServletRequest req = request;
        final ServletResponse res = response;
        try {
            java.security.AccessController.doPrivileged(
                    (java.security.PrivilegedExceptionAction<Void>) () -> {
                        internalDoFilter(req,res);
                        return null;
                    }
            );
        } catch( PrivilegedActionException pe) {
            Exception e = pe.getException();
            if (e instanceof ServletException)
                throw (ServletException) e;
            else if (e instanceof IOException)
                throw (IOException) e;
            else if (e instanceof RuntimeException)
                throw (RuntimeException) e;
            else
                throw new ServletException(e.getMessage(), e);
        }
    } else {
        internalDoFilter(request,response);
    }
}

从中可以看到ApplicationFilterChain的doFilter方法主要调用了internalDoFilter()

org.apache.catalina.core.ApplicationFilterChain.internalDoFilter()

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);
        }
    }
}

执行Servlet

ApplicationFilterChain.internalDoFilter()中得知,最后会执行Servlet的service方法,此service方法实际是所有Servlet的父类HttpServlet实现的

javax.servlet.http.HttpServlet.service()

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {

    HttpServletRequest  request;
    HttpServletResponse response;

    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException(lStrings.getString("http.non_http"));
    }
    service(request, response);
}

service方法调用重载的service方法,后者通过判断HttpServletRequest对象的HTTP Method,调用不同的方法,如GET、DELETE、POST等

javax.servlet.http.HttpServlet.service()

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                // Invalid date header - proceed as if none was set
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

列举doGet方法

javax.servlet.http.HttpServlet.doGet()

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String msg = lStrings.getString("http.method_get_not_supported");
    sendMethodNotAllowed(req, resp, msg);
}

private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
    String protocol = req.getProtocol();
    // Note: Tomcat reports "" for HTTP/0.9 although some implementations
    //       may report HTTP/0.9
    if (protocol.length() == 0 || protocol.endsWith("0.9") || protocol.endsWith("1.0")) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    }
}

从中可知doGet方法的实现只是返回了400和405错误,是因为这是抽象类HttpServlet的默认实现,用户必须实现自身的Servlet或者使用默认的DefaultServlet

至此,Tomcat请求原理已分析完毕

最后附上一张调用链路图,来自Tomcat官网

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值