学Springboot必须要懂的内嵌式Tomcat启动与请求处理

前言

找了一圈也没有找到合适内置容器源码解读的文章,就打算自己写一篇,方便后面阅读加深映像
Springboot相对于以前的Spring省去了很多配置xml的麻烦,而且还支持使用内嵌式的Servlet容器,非常的便利。既然这样,为了精准快速的定位问题,以及满足我们膨胀的好奇心,那我们就有必要了解便利的背后是什么操作了,学习高效的策略以便于提升自己嘛,下面我们用Tomcat来探索一下。篇幅较长,请耐心阅读,一定会有收获的。
还可以阅读一下我的另外一篇文章《自己实现Servlet---我自己的“tomcat”》
),一定能够更好的理解web容器。

章节说明

1.内嵌式tomcat配置
配置项分析
2.启动流程
(1)启动主流程
(2)NioEndpoint启动监听
3.处理Http请求
一次http请求的生命周期

一、内嵌式tomcat配置

// 在类org.springframework.boot.autoconfigure.web.ServerProperties可以找到tomcat的相关配置参数
// 日志配置
private final Accesslog accesslog = new Accesslog();
// 授信的地址前缀
private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
                + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
                + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
                + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
                + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
                + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|"
                + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" //
                + "0:0:0:0:0:0:0:1|::1";

// 请求头协议,一般为"X-Forwarded-Proto"
private String protocolHeader;
// ssl的头协议https
private String protocolHeaderHttpsValue = "https";
// 请求的原始端口号
private String portHeader = "X-Forwarded-Port";
// 请求源ip
private String remoteIpHeader;
// tomcat的文件基地址
private File basedir;
// 定期调用backgroundProcessor方法。backgroundProcessor方法主要用于重新加载Web应用程序的类文件和资源、扫描Session过期
@DurationUnit(ChronoUnit.SECONDS)
private Duration backgroundProcessorDelay = Duration.ofSeconds(10);
// 并行线程的最大值
private int maxThreads = 200;
// 最小的闲置线程数
private int minSpareThreads = 10;
// post请求的数据最大值
private DataSize maxHttpPostSize = DataSize.ofMegabytes(2);
// 请求头的最大值,如果是0,则取默认的8kb
private DataSize maxHttpHeaderSize = DataSize.ofBytes(0);
// 请求体数据的最大值
private DataSize maxSwallowSize = DataSize.ofMegabytes(2);
// 请求跳转时是否带上上下文
private Boolean redirectContextRoot = true;
// 是否使用相对的跳转路径
private Boolean useRelativeRedirects;
// 编码格式
private Charset uriEncoding = StandardCharsets.UTF_8;
// 最大的连接数10000
private int maxConnections = 10000;
// 当所有线程在忙的时候,等待队列中请求个数
private int acceptCount = 100;

二、启动流程

2.1 主流程

image.png

2.2 TomcatServletWebServerFactory

顾名思义它是一个TomcatWebServer的生产工厂,它内部有一个getWebServer的方法,是这个工厂用来生产WebServer的,然后我们需要查看一下它的类继承结构,不然理解上会断层


继承结构.JPG

继承结构图中,我们可以看到它实现了ServletWebServerFactory这个接口。然后我们转到Springboot其中过程中的onRefresh方法中

protected void onRefresh() {
        super.onRefresh();
        try {// 创建WebServer
            createWebServer();
        }
        catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
private void createWebServer() {
        WebServer webServer = this.webServer;
        // 获取容器的上下文信息
        ServletContext servletContext = getServletContext();
        // 上下文对象和server对象都还没有初始化的话,通过工厂进行生成webserver
        if (webServer == null && servletContext == null) {
            // 从spring容器中获取server的生产工厂
            ServletWebServerFactory factory = getWebServerFactory();
            // 调用工厂的方法去生产server
            // getSelfInitializer采用了lamda表达式去实现了初始化方法,下面稍微说一下
            this.webServer = factory.getWebServer(getSelfInitializer());
        }
        // 如果上下文不是空的,那么可能就是tomcat初始化失败了,可以使用上下文去初始化tomcat
        // 小小的推敲一下,说明tomcat的启动的基本信息都可以在context里面找到
        else if (servletContext != null) {
            try {
                getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context",
                        ex);
            }
        }
        initPropertySources();
    }
    protected ServletWebServerFactory getWebServerFactory() {
        // 从容器中获取所有实现了ServletWebServerFactory接口的bean,刚才我们看了类结构
        // TomcatServletWebServerFactory是实现了ServletWebServerFactory,如果它被容器加载了,然后这里就能获取到它了
        String[] beanNames = getBeanFactory()
                .getBeanNamesForType(ServletWebServerFactory.class);
        // spring容器中必须要存在一个实现接口ServletWebServerFactory的bean
        if (beanNames.length == 0) {
            throw new ApplicationContextException(
                    "Unable to start ServletWebServerApplicationContext due to missing "
                            + "ServletWebServerFactory bean.");
        }
        // 如果有好几个实现的bean,也会报错,只允许存在一个,说明在springboot里面是不支持多个servlet容器并存的
        if (beanNames.length > 1) {
            throw new ApplicationContextException(
                    "Unable to start ServletWebServerApplicationContext due to multiple "
                            + "ServletWebServerFactory beans : "
                            + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
        return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
// 看到这里也许有人会疑惑,方法是有返回值的,怎么它返回了void
// 其实它是使用了lamda的匿名实现方式,换种写法可能更容易理解return servletContext->selfInitialize(servletContext)
        return this::selfInitialize;
    }

private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareWebApplicationContext(servletContext);
        registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
                servletContext);
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }

好了这里就到了重点了,开始生产webserver

@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    Tomcat tomcat = new Tomcat();
    // 新建一个tomcat对象之后,开始配置参数,配置连接等
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory
            : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    // 这个Connector也是tomcat的核心,等会2.4小节会仔细分析一下
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    // 准备上下文对象
    prepareContext(tomcat.getHost(), initializers);
    // 创建webserver
    return getTomcatWebServer(tomcat);
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    this.tomcat = tomcat;
    this.autoStart = autoStart;
    // 初始化webserver
    initialize();
}

private void initialize() throws WebServerException {
    logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
        try {
            addInstanceIdToEngineName();

            Context context = findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource())
                        && Lifecycle.START_EVENT.equals(event.getType())) {
                    // 将连接从service中的连接移除,并转存到serviceConnectors这个map中
                    removeServiceConnectors();
                }
            });

            // 启动tomcat,并触发启动监听事件,然后做一下StandardServer对象的初始化
            this.tomcat.start();

            // 我们可以在这里重新抛出主线程中的异常
            rethrowDeferredStartupExceptions();

            try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(),
                        getClass().getClassLoader());
            }
            catch (NamingException ex) {
                // Naming is not enabled. Continue
            }

            // 跟jetty不一样的是,tomcat里面的线程都是守护线程,主线程停止,子线程当即停止
            startDaemonAwaitThread();
        }
        catch (Exception ex) {
            stopSilently();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

2.3 TomcatWebServerFactoryCustomizer

这个类是通过EmbeddedWebServerFactoryCustomizerAutoConfiguration加载进来的,不得不说这个类名是真的长,它主要就是将配置信息加载进来,在onRefresh之前加载。

// 这个方法有个入参,需要实现了接口ConfigurableTomcatWebServerFactory的bean,如果还没有忘记上面的类结构的话,你就会发现刚才的TomcatServletWebServerFactory也继承并实现了它。简而言之,就是把配置信息加载到TomcatServletWebServerFactory对象中。然后onRefresh的时候才能
public void customize(ConfigurableTomcatWebServerFactory factory) {
        ServerProperties properties = this.serverProperties;
        ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
        // 这个方法里面主要都是配置相关的,没什么特别的
        // 不过PropertyMapper这个类的from when to的写法很有意思,可以模仿一下
        PropertyMapper propertyMapper = PropertyMapper.get();
        propertyMapper.from(tomcatProperties::getBasedir).whenNonNull()
                .to(factory::setBaseDirectory);
        propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull()
                .as(Duration::getSeconds).as(Long::intValue)
                .to(factory::setBackgroundProcessorDelay);
        customizeRemoteIpValve(factory);
        propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive)
                .to((maxThreads) -> customizeMaxThreads(factory,
                        tomcatProperties.getMaxThreads()));
        propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive)
                .to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads));
        propertyMapper.from(this::determineMaxHttpHeaderSize).whenNonNull()
                .asInt(DataSize::toBytes).when(this::isPositive)
                .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
                        maxHttpHeaderSize));
        propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull()
                .asInt(DataSize::toBytes)
                .to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
        propertyMapper.from(tomcatProperties::getMaxHttpPostSize).asInt(DataSize::toBytes)
                .when((maxHttpPostSize) -> maxHttpPostSize != 0)
                .to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
                        maxHttpPostSize));
        propertyMapper.from(tomcatProperties::getAccesslog)
                .when(ServerProperties.Tomcat.Accesslog::isEnabled)
                .to((enabled) -> customizeAccessLog(factory));
        propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull()
                .to(factory::setUriEncoding);
        propertyMapper.from(properties::getConnectionTimeout).whenNonNull()
                .to((connectionTimeout) -> customizeConnectionTimeout(factory,
                        connectionTimeout));
        propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive)
                .to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
        propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive)
                .to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
        customizeStaticResources(factory);
        customizeErrorReportValve(properties.getError(), factory);
    }

2.4 Connector及其相关
上面的TomcatServletWebServerFactory中有写到Connector的初始化,下面是我摘出来的跟Connector初始化有关的部分,可以看到默认都是采用Http11NioProtocol这个protocol协议

public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
private String protocol = DEFAULT_PROTOCOL;
Connector connector = new Connector(this.protocol);
....
// 然后给tomcat对象设置一下connector
tomcat.setConnector(connector);
 public Connector(String protocol) {
        boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
                AprLifecycleListener.getUseAprConnector();
        if ("HTTP/1.1".equals(protocol) || protocol == null) {
            if (aprConnector) {
                protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
            } else {
            //好了,我们采用的就是这个,证据充分
                protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
            }
        } else if ("AJP/1.3".equals(protocol)) {
            if (aprConnector) {
                protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
            } else {
                protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
            }
        } else {
            protocolHandlerClassName = protocol;
        }

        // Instantiate protocol handler
        ProtocolHandler p = null;
        try {
            // 获取Http11NioProtocol的class对象
            Class<?> clazz = Class.forName(protocolHandlerClassName);
            // 反射实例化对象
            // Http11NioProtocol的构造函数里面实例化了一个NioEndpoint对象
            p = (ProtocolHandler) clazz.getConstructor().newInstance();
        } catch (Exception e) {
            log.error(sm.getString(
                    "coyoteConnector.protocolHandlerInstantiationFailed"), e);
        } finally {
            // 将实例化的对象赋值给属性,后面容器启动要用到
            this.protocolHandler = p;
        }

        // Default for Connector depends on this system property
        setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
    }

Springboot调用finishRefresh()方法会去启动webServer

private WebServer startWebServer() {
    WebServer webServer = this.webServer;
    if (webServer != null) {
        webServer.start();
    }
    return webServer;
}
@Override
public void start() throws WebServerException {
    synchronized (this.monitor) {
        if (this.started) {
            return;
        }
        try {
            // 把一开始转存到serviceConnectors这个map中的connector再加回来
            addPreviouslyRemovedConnectors();
            Connector connector = this.tomcat.getConnector();
            if (connector != null && this.autoStart) {
                performDeferredLoadOnStartup();
            }
            checkThatConnectorsHaveStarted();
            this.started = true;
            logger.info("Tomcat started on port(s): " + getPortsDescription(true)
                    + " with context path '" + getContextPath() + "'");
        }
    .........多余的代码就不展示了
    }
}
private void addPreviouslyRemovedConnectors() {
        Service[] services = this.tomcat.getServer().findServices();
        for (Service service : services) {
        // serviceConnectors里面存放的就是一开始被移除的connector
            Connector[] connectors = this.serviceConnectors.get(service);
            if (connectors != null) {
                for (Connector connector : connectors) {
                    // 这里就是重点了,给服务加上连接,但是它不光具备add功能,它在方法内部启动了connector
                    service.addConnector(connector);
                    if (!this.autoStart) {
                        stopProtocolHandler(connector);
                    }
                }
                this.serviceConnectors.remove(service);
            }
        }
    }

添加连接到连接数组中,并启动连接

 @Override
    public void addConnector(Connector connector) {

        synchronized (connectorsLock) {
        // 可以发现连接是用的数组存储的,每次增加都需要进行数组拷贝
            connector.setService(this);
            Connector results[] = new Connector[connectors.length + 1];
            System.arraycopy(connectors, 0, results, 0, connectors.length);
            results[connectors.length] = connector;
            connectors = results;
        }

        try {
            if (getState().isAvailable()) {
            // 这里才是我们的重点,启动connector;
            // connector也是继承了LifecycleBase的,查看LifecycleBase的start方法。
                connector.start();
            }
        } catch (LifecycleException e) {
            throw new IllegalArgumentException(
                    sm.getString("standardService.connector.startFailed", connector), e);
        }
        // Report this property change to interested listeners
        support.firePropertyChange("connector", null, connector);
    }
@Override
public final synchronized void start() throws LifecycleException {
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {
        // 打印日志
        if (log.isDebugEnabled()) {
            Exception e = new LifecycleException();
            log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
        } else if (log.isInfoEnabled()) {
            log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
        }
        return;
     }
     // 根据状态执行对应的方法,这里面不做展开
     if (state.equals(LifecycleState.NEW)) {
         init();
     } else if (state.equals(LifecycleState.FAILED)) {
         stop();
     } else if (!state.equals(LifecycleState.INITIALIZED) &&
             !state.equals(LifecycleState.STOPPED)) {
         invalidTransition(Lifecycle.BEFORE_START_EVENT);
     }
     try {
         setStateInternal(LifecycleState.STARTING_PREP, null, false);
         // 好了这个就是我们的重点了,在LifecycleBase中它是一个抽象方法,所以直接看Connector里面的方法实现
         startInternal();
         if (state.equals(LifecycleState.FAILED)) {
             // This is a 'controlled' failure. The component put itself into the
             // FAILED state so call stop() to complete the clean-up.
             stop();
         } else if (!state.equals(LifecycleState.STARTING)) {
             // Shouldn't be necessary but acts as a check that sub-classes are
             // doing what they are supposed to.
             invalidTransition(Lifecycle.AFTER_START_EVENT);
         } else {
             setStateInternal(LifecycleState.STARTED, null, false);
         }
        } catch (Throwable t) {
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            handleSubClassException(t, "lifecycleBase.startFail", toString());
        }
}
@Override
protected void startInternal() throws LifecycleException {
     // Validate settings before starting
    if (getPortWithOffset() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
    }
    setState(LifecycleState.STARTING);
    try {
    // 这个handler刚才已经赋值过了,是Http11NioProtocol的实例化对象
        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

然后我们就来分析一下Http11NioProtocol,它的start隐藏了什么,这个start方法位于它的父类AbstractProtocol中

 @Override
    public void start() throws Exception {
        if (getLog().isInfoEnabled()) {
            getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
            logPortOffset();
        }
        // 启动endpoint,再往里面看,快到底了
        endpoint.start();
        // 观察超时
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
                new Runnable() {
                    @Override
                    public void run() {
                        if (!isPaused()) {
                            startAsyncTimeout();
                        }
                    }
                }, 0, 60, TimeUnit.SECONDS);
    }
    // start中的内容如下,主要有两个步骤bindWithCleanup和startInternal
   public final void start() throws Exception {
        if (bindState == BindState.UNBOUND) {
        // 这个方法里面调用了一个bind方法,但是是个抽象方法,因为我们前面新建的是NioEndPoint对象,所以去看它的bind实现
            bindWithCleanup();
            bindState = BindState.BOUND_ON_START;
        }
        startInternal();
    }

下面是NioEndPoint两个启动的核心方法了

 @Override
    public void bind() throws Exception {
        // 这里就证明了HTTP也是封装了socket
        // 不过这里还没有开始接收请求呢,要等下面的方法才开启接收
        initServerSocket();
        setStopLatch(new CountDownLatch(1));
        // 如果必要的话初始化一下ssl
        initialiseSsl();
        selectorPool.open(getName());
    }
protected void initServerSocket() throws Exception {
        if (!getUseInheritedChannel()) {
            // 初始化开启serverSocket的通道
            serverSock = ServerSocketChannel.open();
            socketProperties.setProperties(serverSock.socket());
            InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
            serverSock.socket().bind(addr,getAcceptCount());
        } else {
            // 系统的通道
            Channel ic = System.inheritedChannel();
            if (ic instanceof ServerSocketChannel) {
                serverSock = (ServerSocketChannel) ic;
            }
            if (serverSock == null) {
                throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
            }
        }
        // serverSock.configureBlocking(true);它设置成了阻塞模式
        // 后面开启accept就会一直阻塞到由请求过来
        serverSock.configureBlocking(true); //mimic APR behavior
    }
@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());
            }
            // 创建工作线程池
            if (getExecutor() == null) {
                createExecutor();
            }
            // 初始化连接限制,默认 maxConnections=10000
            initializeConnectionLatch();

            // 启动轮询线程,内部有一个通道选择器,selector和socketChannel要绑定一下,可以补一下nio的相关知识哦
            poller = new Poller();
            Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();
            // 新建Acceptor对象,Acceptor是实现了Runnable接口的
            // 它的run方法实现了最终调用了serverSock.accept()
            // 但是initServerSocket里面设置了阻塞模式,当没有事件进来的时候会一直阻塞
            // 每当有请求进来的时候就会组装报文对象---NioSocketWrapper
            startAcceptorThread();
        }
    }

推荐可以仔细的阅读一下NioEndPoint,类的位置在package org.apache.tomcat.util.net中,尤其是里面的几个内部类如:PollerEvent轮询事件,Poller轮询器,NioSocketWrapper对socket的读写包装,SocketProcessor是scoket的处理器

因为上面只说到了socketChannel的初始化和accept,没有将scoketChannel和selector绑定,所以我们展开说一下Poller,它实现了Runnable接口,上面的流程中有: pollerThread.start(),所以我们进去看它的run方法

  @Override
        public void run() {
            // 死循环,直到线程死亡,或者被中断
            while (true) {
            // 设置事件标志,默认是没有事件
                boolean hasEvents = false;
                try {
                    if (!close) {
                    // events方法在下面有展开
                        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);
                    }
                    //....................................................
                   // 在不影响阅读的情况下省略了部分代码,有需要的话可以去看一下源码
                Iterator<SelectionKey> iterator =
                    keyCount > 0 ? selector.selectedKeys().iterator() : null;
            // 下面的流程就是tomcat处理http请求的流程了
            // processKey这个方法等会会详细分析
            while (iterator != null && iterator.hasNext()) {
                SelectionKey sk = iterator.next();
                NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                // Attachment may be null if another thread has called
                // cancelledKey()
                if (socketWrapper == null) {
                    iterator.remove();
                } else {
                    iterator.remove();
                    processKey(sk, socketWrapper);
                }
            }
            // 处理超时问题
            timeout(keyCount,hasEvents);
        }
        getStopLatch().countDown();
    }

 public boolean events() {
            boolean result = false;
            PollerEvent pe = null;
            for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
            // 有事件的时候返回值就是true了
                result = true;
                try {
                // 这个地方发现事件继承了Runnable接口其实是个幌子,它直接调用了run方法,真尴尬
                    pe.run();
                    pe.reset();
                    if (running && !paused && eventCache != null) {
                        eventCache.push(pe);
                    }
                } catch ( Throwable x ) {
                    log.error(sm.getString("endpoint.nio.pollerEventError"), x);
                }
            }
            return result;
        }
 @Override
public void run() {
        if (interestOps == OP_REGISTER) {
            try {
            // 这里通道就和selector绑定了,产生SelectionKey对象,存在SelectorImpl的Set中
            // poller的run方法中就可以获取到它了,然后processKey方法就可以处理请求了
                socket.getIOChannel().register(socket.getSocketWrapper().getPoller().getSelector(),
                SelectionKey.OP_READ, socket.getSocketWrapper());
            } catch (Exception x) {
                log.error(sm.getString("endpoint.nio.registerFail"), x);
            }
        } else {
        // 这里的代码就不展示了,因为我们上面 有这个步骤 r.reset(socket, OP_REGISTER),所以if()条件一定成立
        }
    }

三、处理Http请求

image.png

第三节分三个部分(1)请求预处理(2)请求解析(3)请求处理

3.1 请求预处理

请求预处理中我们将会看到,tomcat是怎么做解析准备,然后怎么提交给线程池处理请求的
上面的Poller的run方法中还有一段代码没解释,其中有一个步骤: processKey(sk, socketWrapper),这个步骤就是开始处理http请求了

protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
// 这个方法里面没有做任何相关的请求解析
// 做了三个判断close(endPoint被关闭),isReadable(是否读操作),getSendfileData(是否发送文件)
// 然后调用如下方法
       processSocket(socketWrapper, SocketEvent.OPEN_READ, true)
    }
// processSocket也只是创建了Processor,并通过开始创建好的线程池给Processor分配线程
 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) {
            // 新建一个处理器,下面我们展开看看SocketProcessor类
                sc = createSocketProcessor(socketWrapper, event);
            } else {
                sc.reset(socketWrapper, event);
            }
            Executor executor = getExecutor();
            // 也可以设置不分配,不分配的直接调用处理器的run方法
            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的类信息详细解释一下

 protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
        public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
            super(socketWrapper, event);
        }
        // 父类中的run方法会调用它
        @Override
        protected void doRun() {
            NioChannel socket = socketWrapper.getSocket();
            SelectionKey key = socket.getIOChannel().keyFor(socket.getSocketWrapper().getPoller().getSelector());
            Poller poller = NioEndpoint.this.poller;
            if (poller == null) {
                socketWrapper.close();
                return;
            }
            try {
                int handshake = -1;
                try {
                // 状态标志设置
                    if (key != null) {
                        if (socket.isHandshakeComplete()) {
                            handshake = 0;
                        } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                                event == SocketEvent.ERROR) {
                            handshake = -1;
                        } else {
                        // 这里handshake=0,正常流程都进到了这里
                            handshake = socket.handshake(key.isReadable(), key.isWritable());
                            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;
                    // 处理来自socket的请求
                    if (event == null) {
                        state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                    } else {
                    // 然后Handler去处理事件报文了,下一个小节我们去找一下ConnectorHandler
                    // 看它是怎么处理的
                        state = getHandler().process(socketWrapper, event);
                    }
                    if (state == SocketState.CLOSED) {
                        poller.cancelledKey(key, socketWrapper);
                    }
                } else if (handshake == -1 ) {
                    poller.cancelledKey(key, socketWrapper);
                } else if (handshake == SelectionKey.OP_READ){
                    socketWrapper.registerReadInterest();
                } else if (handshake == SelectionKey.OP_WRITE){
                    socketWrapper.registerWriteInterest();
                }
            } catch (CancelledKeyException cx) {
                poller.cancelledKey(key, socketWrapper);
            } catch (VirtualMachineError vme) {
                ExceptionUtils.handleThrowable(vme);
            } catch (Throwable t) {
                log.error(sm.getString("endpoint.processing.fail"), t);
                poller.cancelledKey(key, socketWrapper);
            } finally {
                socketWrapper = null;
                event = null;
                //return to cache
                if (running && !paused && processorCache != null) {
                    processorCache.push(this);
                }
            }
        }
    }

注意这个process方法真的特别长,有250多行,为了避免方法过长影响理解,我将有些非主流程的代码用中文注释带过了

public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
            // (1)打印debug日志,socket的事件状态日志
            // (2)wrapper判断是否为空,为空直接 return SocketState.CLOSED;
            S socket = wrapper.getSocket();
            // 这个东西我尝试相同的同步请求,请求了几次,发现每次都是拿到空的对象
            // 然后发现是异步的才会可能获取到
            // 下面有提到connections是用来建立socket和processor联系的map
            Processor processor = connections.get(socket);
            // (3)打印debug日志,processor的相关日志
            // 超时情形下三种会触发:
            // a).processor为空,
            // b).processor是异步的,还有一种
            // c).Async 的超时用另外一个线程来计算和处理
            // 然后因为是异步处理的,有时候超时的话就不再需要反馈信息
            // 这里检查一下,避免再去处理
            if (SocketEvent.TIMEOUT == status && (processor == null ||
                    !processor.isAsync() || !processor.checkAsyncTimeoutGeneration())) {
                // 这是一个有效的空指令
                return SocketState.OPEN;
            }
            // 满足processor是同步的和processor需要超时信息的反馈
            if (processor != null) {
                // 确保异步超时不会被触发
                getProtocol().removeWaitingProcessor(processor);
            } else if (status == SocketEvent.DISCONNECT || status == SocketEvent.ERROR) {
                // 连接断开,或者异常了,就不做处理,直接返回关闭的socket状态
                return SocketState.CLOSED;
            }
            // 容器做一下标记
            ContainerThreadMarker.set();
            try {
                if (processor == null) {
                    String negotiatedProtocol = wrapper.getNegotiatedProtocol();
                    // OpenSSL 通常返回空, JSSE通常返回“”,如果没有商定协议的话
                    if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) {
                        UpgradeProtocol upgradeProtocol =
                                getProtocol().getNegotiatedProtocol(negotiatedProtocol);
                        if (upgradeProtocol != null) {
                            processor = upgradeProtocol.getProcessor(
                                    wrapper, getProtocol().getAdapter());
                        } else if (negotiatedProtocol.equals("http/1.1")) {
                            // 这里不做处理,在下面获取processor,http1.1是默认的协议
                        } else {
                            // openssl 1.0.2的alpn回调不支持握手失败,如果无法协商任何协议
                            // 则会出现错误。因此,我们需要断开这里的连接。
                            if (getLog().isDebugEnabled()) {
                                getLog().debug(sm.getString(
                                    "abstractConnectionHandler.negotiatedProcessor.fail",
                                    negotiatedProtocol));
                            }
                            return SocketState.CLOSED;
                            /*
                             * 如果使用了openssl 1.1.0那我们用这里的代码块替换上面的代码
                            // Failed to create processor. This is a bug.
                            throw new IllegalStateException(sm.getString(
                                    "abstractConnectionHandler.negotiatedProcessor.fail",
                                    negotiatedProtocol));
                            */
                        }
                    }
                }
                // 使用了http 1.1协议的来到了这里
                if (processor == null) {
                    processor = recycledProcessors.pop();
                    // (4)打印debug日志,processor的出栈日志
                }
                if (processor == null) {
                // 没有拿到处理器,则创建一个,并注册,这里我们新建的是Http11Processor对象
                    processor = getProtocol().createProcessor();
                    register(processor);
                }
                // 不是ssl连接的话,里面的对象是空的
                processor.setSslSupport(
                        wrapper.getSslSupport(getProtocol().getClientCertProvider()));

                // 建立socket和processor的映射关系
                connections.put(socket, processor);
                SocketState state = SocketState.CLOSED;
                do {
                // 这里面就要处理报文了,也会返回给远程端返回结果
                    state = processor.process(wrapper, status);
                    if (state == SocketState.UPGRADING) {
                      // 正常情况下不会进到这里,代码就不展示了,有兴趣可以看一下源码
                      // 里面是处理状态是SocketState.UPGRADING的代码逻辑
                    }
                } while ( state == SocketState.UPGRADING);

                if (state == SocketState.LONG) {
                    // 在长轮询中获取结果时,保持socket和processor的联系
                    longPoll(wrapper, processor);
                    if (processor.isAsync()) {
                        getProtocol().addWaitingProcessor(processor);
                    }
                } else if (state == SocketState.OPEN) {
                    // 一般的http请求都会进到这里,轮询下一个请求
                    connections.remove(socket);
                    release(processor);
                    wrapper.registerReadInterest();
                } else if (state == SocketState.SENDFILE) {
                    // 如果是还在发送文件,则需要等文件发送完
                }
           // 再次省略部分代码
            } finally {
            // 清除标记
                ContainerThreadMarker.clear();
            }
            // 移除socket和processor的联系
            connections.remove(socket);
            // 回收processor
            release(processor);
            return SocketState.CLOSED;
        }

3.2 请求解析

内嵌的tomcat中有两种处理器:AjpProcessor和Http11Processor,因为我们采用的http1.1协议,采用Http11Processor,类结构如下:


Http11Processor.JPG

processor的报文处理方法process,在AbstractProcessorLight这个抽象类中

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();
                state = dispatch(nextDispatch.getSocketStatus());
            } else if (status == SocketEvent.DISCONNECT) {
                // 断开连接了就不用管了
            } else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
                state = dispatch(status);
                if (state == SocketState.OPEN) {
                    // 可能存在管道数据需要读取
                    state = service(socketWrapper);
                }
            } else if (status == SocketEvent.OPEN_WRITE) {
                // 异步处理后提取写事件
                state = SocketState.LONG;
            } else if (status == SocketEvent.OPEN_READ){
            // 因为我们前面SocketEvent是OPEN_READ,所以直接看这里
            // 这里socketWrapper还没有转成request和response
                state = service(socketWrapper);
            } else {
                // 默认处理,关闭socket
                state = SocketState.CLOSED;
            }
            // ....................
            // 打日志之类的处理就省略了
        } while (state == SocketState.ASYNC_END ||
                dispatches != null && state != SocketState.CLOSED);
        return state;
    }

在看Http11Processor类里面的service方法,又是一个200多行的超长方法
这些解析的方法行数都特别多
http请求分为三个部分,请求行+请求头+请求体

public SocketState service(SocketWrapperBase<?> socketWrapper)
        throws IOException {
        // request = new Request();
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
        // 初始化io
        setSocketWrapper(socketWrapper);
        inputBuffer.init(socketWrapper);
        outputBuffer.init(socketWrapper);
        // 初始化标志
        keepAlive = true;
        openSocket = false;
        readComplete = true;
        boolean keptAlive = false;
        SendfileState sendfileState = SendfileState.DONE;
        while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
                sendfileState == SendfileState.DONE && !protocol.isPaused()) {
            // 处理请求头
            try {
            // 解析请求行的过程就在parseRequestLine这个方法里
                if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(),
                        protocol.getKeepAliveTimeout())) {
                    if (inputBuffer.getParsingRequestLinePhase() == -1) {
                        return SocketState.UPGRADING;
                    } else if (handleIncompleteRequestLineRead()) {
                        break;
                    }
                }
                if (protocol.isPaused()) {
                    // 503 - Service unavailable
                    response.setStatus(503);
                    setErrorState(ErrorState.CLOSE_CLEAN, null);
                } else {
                    keptAlive = true;
                    // 请求头中的数据条目数默认最大100条
                    request.getMimeHeaders().setLimit(protocol.getMaxHeaderCount());
                    // 解析请求头
                    if (!inputBuffer.parseHeaders()) {
                        //解析到一半的情况
                        openSocket = true;
                        readComplete = false;
                        break;
                    }
                    if (!protocol.getDisableUploadTimeout()) {
                        socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());
                    }
                }
            } catch (IOException e) {
                // 打印debug日志,代码省略
                setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
                break;
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                UserDataHelper.Mode logMode = userDataHelper.getNextMode();
                if (logMode != null) {
                    String message = sm.getString("http11processor.header.parse");
                    switch (logMode) {
                        case INFO_THEN_DEBUG:
                            message += sm.getString("http11processor.fallToDebug");
                            //$FALL-THROUGH$
                        case INFO:
                            log.info(message, t);
                            break;
                        case DEBUG:
                            log.debug(message, t);
                    }
                }
                // 400 - Bad Request
                response.setStatus(400);
                setErrorState(ErrorState.CLOSE_CLEAN, t);
            }
            // ......................
            // 请求有upgrade的情况处理,代码不展示

            if (getErrorState().isIoAllowed()) {
                // 设置过滤器,并处理一些请求头
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                // 准备request,处理编码啊,异常,长度等情况
                // 在Http11Processor初始化的过程中,会新建很多过滤器
                // 过滤器存在一个数组中,第一个过滤器是IdentityInputFilter,默认就采用它
                // 在后面需要取用请求体的时候才真正的解析,这里只是选用那个解析过滤器
                    prepareRequest();
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("http11processor.request.prepare"), t);
                    }
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    setErrorState(ErrorState.CLOSE_CLEAN, t);
                }
            }
            // 同时存活的请求上限处理,默认100,代码省略
            if (getErrorState().isIoAllowed()) {
               rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
               //处理请求,这里socketWrapper已经转成request和response了
               getAdapter().service(request, response);
            }
          //request收尾工作,代码省略
    }

请求行解析的详细过程如下,请求头的解析过程类似,就不详细展开了

boolean parseRequestLine(boolean keptAlive, int connectionTimeout, int keepAliveTimeout)
            throws IOException {
        // 是否正在解析
        if (!parsingRequestLine) {
            return true;
        }
        // 跳过空行,\r或者\n都表示空行
        if (parsingRequestLinePhase < 2) {
            byte chr = 0;
            do {
                // 读取后面的bytes
                if (byteBuffer.position() >= byteBuffer.limit()) {
                    if (keptAlive) {
                        // 不在读入bytes,直接设置keepAliveTimeout
                        wrapper.setReadTimeout(keepAliveTimeout);
                    }
                    // byteBuffer的position和limit设置
                    if (!fill(false)) {
                        // 读取处于挂起状态,因此不再处于初始状态
                        parsingRequestLinePhase = 1;
                        return false;
                    }
                    // 至少一个byte收到了,设置一下connectionTimeout
                    wrapper.setReadTimeout(connectionTimeout);
                }
                if (!keptAlive && byteBuffer.position() == 0 && byteBuffer.limit() >= CLIENT_PREFACE_START.length - 1) {
                    boolean prefaceMatch = true;
                    for (int i = 0; i < CLIENT_PREFACE_START.length && prefaceMatch; i++) {
                        if (CLIENT_PREFACE_START[i] != byteBuffer.get(i)) {
                            prefaceMatch = false;
                        }
                    }
                    if (prefaceMatch) {
                        // 匹配到HTTP/2的序言
                        parsingRequestLinePhase = -1;
                        return false;
                    }
                }
                // 设置一下request的起始时间
                if (request.getStartTime() < 0) {
                    request.setStartTime(System.currentTimeMillis());
                }
                // 继续读取byteBuffer中的值
                chr = byteBuffer.get();
                // \r或者\n都表示空行
            } while ((chr == Constants.CR) || (chr == Constants.LF));
            // 位置往前挪一位
            byteBuffer.position(byteBuffer.position() - 1);
            parsingRequestLineStart = byteBuffer.position();
            parsingRequestLinePhase = 2;
            // 接收到的请求打印出来,默认的编码格式ISO_8859_1
            if (log.isDebugEnabled()) {
                log.debug("Received ["
                        + new String(byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining(),
                         StandardCharsets.ISO_8859_1) + "]");
            }
        }
        if (parsingRequestLinePhase == 2) {
            // 读取请求方法名,如POST,GET
            boolean space = false;
            while (!space) {
                if (byteBuffer.position() >= byteBuffer.limit()) {
                    if (!fill(false)) // byteBuffer的position和limit设置
                        return false;
                }
                // Spec says method name is a token followed by a single SP but
                // also be tolerant of multiple SP and/or HT.
                int pos = byteBuffer.position();
                byte chr = byteBuffer.get();
                if (chr == Constants.SP || chr == Constants.HT) {
                    space = true;
                    // 这里获取到请求方法名
                    request.method().setBytes(byteBuffer.array(), parsingRequestLineStart,
                            pos - parsingRequestLineStart);
                } else if (!HttpParser.isToken(chr)) {
                    byteBuffer.position(byteBuffer.position() - 1);
                    // 避免未知的协议导致异常出现
                    request.protocol().setString(Constants.HTTP_11);
                    throw new IllegalArgumentException(sm.getString("iib.invalidmethod"));
                }
            }
            parsingRequestLinePhase = 3;
        }
        if (parsingRequestLinePhase == 3) {
        // 第三部分是个空格,代码也是重复就不展示了
        }
        if (parsingRequestLinePhase == 4) {
            int end = 0;
            // 读取URI信息
            boolean space = false;
            while (!space) {
                if (byteBuffer.position() >= byteBuffer.limit()) {
                    if (!fill(false))
                        return false;
                }
                int pos = byteBuffer.position();
                byte chr = byteBuffer.get();
                if (chr == Constants.SP || chr == Constants.HT) {
                    space = true;
                    end = pos;
                } else if (chr == Constants.CR || chr == Constants.LF) {
                    // HTTP/0.9处理方式
                    parsingRequestLineEol = true;
                    space = true;
                    end = pos;
                } else if (chr == Constants.QUESTION && parsingRequestLineQPos == -1) {
                    parsingRequestLineQPos = pos;
                } else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(chr)) {
                    // 避免未知的协议导致异常出现
                    request.protocol().setString(Constants.HTTP_11);
                    throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
                } else if (httpParser.isNotRequestTargetRelaxed(chr)) {
                    // 避免未知的协议导致异常出现
                    request.protocol().setString(Constants.HTTP_11);
                    throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
                }
            }
            //请求参数和路径
            if (parsingRequestLineQPos >= 0) {
                request.queryString().setBytes(byteBuffer.array(), parsingRequestLineQPos + 1,
                        end - parsingRequestLineQPos - 1);
                request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
                        parsingRequestLineQPos - parsingRequestLineStart);
            } else {
                request.requestURI().setBytes(byteBuffer.array(), parsingRequestLineStart,
                        end - parsingRequestLineStart);
            }
            parsingRequestLinePhase = 5;
        }
        if (parsingRequestLinePhase == 5) {
         // 第五部分是个空格,代码也是重复就不展示了
        }
        if (parsingRequestLinePhase == 6) {
           //请求行的最后一个部分,是http协议
            while (!parsingRequestLineEol) {
                if (byteBuffer.position() >= byteBuffer.limit()) {
                    if (!fill(false))
                        return false;
                }
                int pos = byteBuffer.position();
                byte chr = byteBuffer.get();
                if (chr == Constants.CR) {
                    end = pos;
                } else if (chr == Constants.LF) {
                    if (end == 0) {
                        end = pos;
                    }
                    parsingRequestLineEol = true;
                } else if (!HttpParser.isHttpProtocol(chr)) {
                    throw new IllegalArgumentException(sm.getString("iib.invalidHttpProtocol"));
                }
            }
            if ((end - parsingRequestLineStart) > 0) {
            // 设置协议信息
                request.protocol().setBytes(byteBuffer.array(), parsingRequestLineStart,
                        end - parsingRequestLineStart);
            } else {
                request.protocol().setString("");
            }
            parsingRequestLine = false;
            parsingRequestLinePhase = 0;
            parsingRequestLineEol = false;
            parsingRequestLineStart = 0;
            return true;
        }
        throw new IllegalStateException(
                "Invalid request line parse phase:" + parsingRequestLinePhase);
    }

3.3 请求处理

service方法在CoyoteAdapter类中,方法中代码行数太多,避免看的难受,就省略部分

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
            throws Exception {
        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);
        if (request == null) {
            // 创建对象
            request = connector.createRequest();
            request.setCoyoteRequest(req);
            response = connector.createResponse();
            response.setCoyoteResponse(res);
            // 请求和返回互相绑定
            request.setResponse(response);
            response.setRequest(request);
            // 记录此时的request和response对象
            req.setNote(ADAPTER_NOTES, request);
            res.setNote(ADAPTER_NOTES, response);
            // 设置请求编码方式
            req.getParameters().setQueryStringCharset(connector.getURICharset());
        }
        if (connector.getXpoweredBy()) {
        // 这个东西通常需要隐藏,不然会显示你的编码语言,不安全
            response.addHeader("X-Powered-By", POWERED_BY);
        }
        boolean async = false;
        boolean postParseSuccess = false;
        req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
        try {
            // Parse and set Catalina and configuration specific request parameters
            postParseSuccess = postParseRequest(req, request, res, response);
            if (postParseSuccess) {
                //检查是否支持异步,并设置参数
                request.setAsyncSupported(
                        connector.getService().getContainer().getPipeline().isAsyncSupported());
                // 使用Container去处理请求
                // getFirst的返回对象是Valve,在Tomcat初始化的时候存放在容器中
                // 在TomcatWebServerFactoryCustomizer这个类中可以看到几个
                // 还有一些是直接通过Pipeline接口注入进来的
                // 请求流程中涉及到的主要有:StandardEngineValve,ErrorReportValve,StandardHostValve
                // AuthenticatorBase,StandardContextValve,StandardWrapperValve
                connector.getService().getContainer().getPipeline().getFirst().invoke(
                        request, response);
            }
            if (request.isAsync()) {
               // 异步的处理方式,不做展开,有兴趣的可以去看一下源码
            } 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的日志,代码省略
            req.getRequestProcessor().setWorkerThreadName(null);
            // 手动回收,减少GC
            if (!async) {
                updateWrapperErrorCount(request, response);
                request.recycle();
                response.recycle();
            }
        }
    }

StandardWrapperValve是pipeline中的最后一个Valve实现类
它的invoke方法内有创建拦截链的信息以及调用拦截链的doFilter

 ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
filterChain.doFilter(request.getRequest(),
                                    response.getResponse());

然后doFilter又调用了internalDoFilter

 private void internalDoFilter(ServletRequest request,
                                  ServletResponse response)
        throws IOException, ServletException {
        // 拦截链是一个数组,一个个执行下去
        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;
        }
        // 然后拦截链执行完了,开始使用
        try {
            if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                lastServicedRequest.set(request);
                lastServicedResponse.set(response);
            }
            if (request.isAsyncSupported() && !servletSupportsAsync) {
                request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
                        Boolean.FALSE);
            }
            // 这里有两个分支,一个是security的,还有一个是直接调用servlet去执行请求的
            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 {
            // 这里就使用HttpServlet去执行请求了
                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);
            }
        }
    }

总结

文章涵盖了tomcat的启动过程和请求处理流程(包括了报文解析),和SpringMvc的相关流程总算可以串起来了,虽然还有很多地方不是特别清楚,但是对后面定位相关问题,肯定是有巨大帮助的。
后面如果自己写一个容器估计还能对它的理解更深一层。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容