水煮Tomcat(四) – 一个请求无聊的一生

134 阅读3分钟

前言

前面介绍过,tomcat中的组件主要分两种,connector和containner,connector主要负责tomcat与客户端通讯,containner主要处理业务;这里大概提一下,connector启动时,会启动相关的Endpoint,端口的绑定和监听,主要是由Endpoint来执行的。

流转图

image.png

组件图

第一章里发布的架构图,也可以对照看一下
image.png

什么是Valve

Valve是与特定容器关联的请求处理组件,一系列的Valve相互关联成一个Pipeline,通过invoke()方法来处理数据。下面是官方注解

A Valve is a request processing component associated with a particular Container.  A series of Valves are generally associated with each other into a Pipeline.  The detailed contract for a Valve is included in the description of the invoke() method below.

HISTORICAL NOTE:  The "Valve" name was assigned to this concept because a valve is what you use in a real world pipeline to control and/or modify flows through it.

步骤

请求的处理主要分为4个步骤:

  1. 监听服务器端口,读取来自客户端的请求;
  2. 将请求数据按照指定的协议进行解析;
  3. 根据请求地址匹配正确的容器进行处理;
  4. 返回客户响应;
    image.png
    下面一一来介绍,文章研究的版本是tomcat9.0.36

接收请求

根据请求中的端口地址来选择连接器,比如http://localhost:8080/xxx,那么就会选择监听8080端口的连接器进行接收处理。
当前版本默认使用的是非阻塞线程模型,分为接收者和执行者,接收者是单个线程,启动之后一直监听;执行者是java线程池,执行具体业务请求;
Nio的接收者是NioEndpoint,一个endpoint中,主要做四件事情:

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

AbstractEndpoint.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();
    }

Acceptor类实现了Runnable中,run方法中有个while死循环,一直等待客户端网络事件;

    @Override
    public void run() {
        int errorDelay = 0;
        // Loop until we receive a shutdown command
        while (endpoint.isRunning()) {
            // ... 忽略一些校验代码
            state = AcceptorState.RUNNING;
            try {
                //if we have reached max connections, wait
                endpoint.countUpOrAwaitConnection();
                U socket = null;
                try {
                    socket = endpoint.serverSocketAccept();
                } catch (Exception ioe) {
                    // We didn't get a socket
                    endpoint.countDownConnection();
                }
                // ... 忽略后置处理
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
            }
        }
        state = AcceptorState.ENDED;
    }

解析协议

将请求数据按照指定的协议进行解析;
Http11Processor.service()

@Override
public SocketState service(SocketWrapperBase<?> socketWrapper)
    try {
			// 解析协议数据
			prepareRequest();
            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
            // 跳到adapter进行处理
            getAdapter().service(request, response);
            if(keepAlive && !getErrorState().isError() && !isAsync() &&
                    statusDropsConnection(response.getStatus())) {
                setErrorState(ErrorState.CLOSE_CLEAN, null);
            }
        } catch (InterruptedIOException e) {
            setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
        }
}

匹配容器

根据请求地址匹配正确的容器进行处理;
CoyoteAdapter.service方法,处理请求并返回,接收httpServlet,向下传递时,发送httpServletServlet。
这一步拿到Service中配置的Engine容器。

// 调用容器【container】
connector.getService().getContainer().getPipeline().getFirst().invoke(
        request, response); 

这里需要介绍一下tomcat中使用到的责任链模式

责任链

tomcat中最基本的元素,集合在pipeline中,每次请求都会执行,类似于filter,在源代码中,预定义了一些valve,access日志也是由valve输出,具体见【org.apache.catalina.valves.AccessLogValve】;
CoyoteAdapter -> StandardEngineValve -> StandardHostValve -> StandardContextValve -> StandardWrapperValve
在StandardWrapperValve的allocate()方法里,会分配一个servlet进行处理请求,默认是抽象类HttpServlet。
可以看看StandardEngineValve是如何调用StandardHostValve的

    public final void invoke(Request request, Response response) {

        // 读取请求中附带的host信息
        Host host = request.getHost();
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        // 调用StandardHostValue实例,执行下一步
        host.getPipeline().getFirst().invoke(request, response);
    }

代码中的【host.getPipeline().getFirst()】,返回的就是StandardHostValve实例。

响应

spring mvc框架中,FrameworkServlet类继承了HttpServlet,进行分发处理,组织业务数据,返回客户端请求;