Tomcat源码分析之请求流程

1,086 阅读6分钟

Filter相信大家都有在项目中使用过,Filter可以对访问的请求和响应进行拦截,可是Filter的原理是怎么样呢?

大家都知道Filter能够按顺序执行,Filter中有3个方法:initdoFilter destroy ,当在doFilter中执行chain.doFilter()时会执行下一个Filter

public interface Filter {

    public void init(FilterConfig filterConfig) throws ServletException;
	
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException;
  
    public void destroy();
}

那么问题来了:

一个请求是如何到达Filter的呢?

调用chain.doFilter()的时候为什么会执行下一个Filter呢?

接下来结合源码看看Filter的执行链路

Tomcat请求处理流程

首先,FilterChain对象从哪儿来的?结合源码分析,可以看到FilterChainTomcat中的实现是ApplicationFilterChain,而ApplicationFilterChain则通过org.apache.catalina.core.ApplicationFilterFactory的createFilterChain()创建,那什么时候调用createFilterChain()去创建FilterChain呢?这就要从Tomcat的体系架构说起了

Tomcat的架构体系

Tomcat提供了Servlet的默认实现,是Servlet运行的容器,在Tomcat Server中,一个Service由两部分组成,分别是Connector(连接器)和Container(容器)

连接器

  • 连接器的职责是创建一个服务器套接字用来监听前来的HTTP请求,读取套接字的输入流,根据具体应用层协议(HTTP/AJP)解析请求体、请求头、cookies等信息,构建ServletRequest对象,调用Servlet容器得到ServletResponse对象并转发成网络字节流返回浏览器

    连接器模块包括3大核心组件:Endpoint(用于处理Socket请求的接收和发送)、Processor (接收来自 Endpoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象)和 Adapter (将 Tomcat Request 转成 ServletRequest,并调用容器),下面以一个http请求为例:
    请求首先会到达Endpoint Endpoint可以理解为TCP/IP协议的实现,Tomcat目前支持NIO、NIO.2、APR三种IO模型,以NIO为例

Endpoint------>NioEndpoint

NioEndpoint有两个比较重要的组件Acceptor和SocketProcessor

Acceptor

    protected class Acceptor extends AbstractEndpoint.Acceptor {

        @Override
        public void run() {
        ......
                        // socket
                        socket = serverSock.accept();
        ......

Acceptor用于监听Socket连接请求

SocketProcessor

  @Override
        protected void doRun() {
            ......
                    if (event == null) {
                        state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                    } else {
                        state = getHandler().process(socketWrapper, event);
                    }
			......

SocketProcessor用于处理接收到的Socket请求,它实现了Runnable接口,在run方法通过getHandler().process(socketWrapper, event)调用Processor协议层组件

Processor------>Http11Processor

Processor是应用层协议的实现,目前Tomcat支持HTTP/1.1、AJP、HTTP/2协议,以 HTTP/1.1为例

public SocketState service(SocketWrapperBase<?> socketWrapper)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

        // Setting up the I/O
        setSocketWrapper(socketWrapper);
        inputBuffer.init(socketWrapper);
        outputBuffer.init(socketWrapper);
		 ......

            if (!getErrorState().isError()) {
                // Setting up filters, and parse some request headers
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                    prepareRequest();
                	......
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    getAdapter().service(request, response);
           			......
                        response.reset();
                        response.setStatus(500);
                        setErrorState(ErrorState.CLOSE_CLEAN, e);
                        response.setHeader("Connection", "close"); // TODO: Remove
     	   			......

Processor 接收来自Endpoint的Socket,读取字节流解析成 Tomcat Request 和 Response 对象,并通过 getAdapter().service(request, response)调用Adapter处理。

Adapter------>CoyoteAdapter

Adapter的作用主要是完成ServletRequest标准对象的转换,以达到解耦的效果

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

    ......

    try {
        // Parse and set Catalina and configuration specific
        // request parameters
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            //check valves if we support async
            request.setAsyncSupported(
                 connector.getService().getContainer().getPipeline().isAsyncSupported());
            // Calling the container
            connector.getService().getContainer().getPipeline().getFirst().invoke(
                    request, response);
        }

Request对象实现了HttpServletRequest接口,Adapter在此完成了Tomcat Request到HttpServletRequest的转换,再调用容器的service方法

容器

容器用于处理并响应用户的一个servlet请求如上图,在Tomcat中共有四种容器:Engine(引擎)、 Host(主机)、 Context(上下文)、Wrapper(包装器)
Engine 可以理解为容器和连接器的桥梁,Engine会接受并处理来自连接器的所有请求,并响应
Host 虚拟主机,表示我们的服务器支持的域名,一个引擎可以包含多个主机
Context 上下文容器,可以看成应用服务,每个Host里面可以运行多个应用服务,每一个上下文具有唯一的路径 Wrapper 是Servlet的抽象和包装,每个Context可以有多个Wrapper,用于支持不同的Servlet

Tomcat提供了server.xml配置文件对容器构成组件进行配置

 <Server>
    <Service>
        <Connector />
         <Connector />
       <Engine>   <!-- Engine组件从一个或多个Connector中接收请求并处理,并将完成的响应返回给Connector -->
          <Host>	<!-- Host是Engine的子容器,Engine组件中可以内嵌1个或多个Host组件 -->
                <Context />  <!-- Context是Host的子容器,每个Host中可以定义任意多的Context元素 -->
            </Host>
        </Engine>
    </Service>
 </Server>

org.apache.catalina.Container接口定义了容器的规范,。Engine、Host、Context和Wrapper接口都实现了Container即可。它们的标准实现是StandardEngine, StandardHost, StandardContext, and StandardWrapper

请求是怎么在层层容器中流转,抵达需要执行的Filter或者Servlet呢?

Pipeline-Valve 管道

在Tomcat容器中,设计者运用责任链模式来处理请求的流转以达到解耦的效果,Pipeline、Valve定义了该设计模式在容器中运用的基本模型。如下图,Pipeline可以理解为管道,而valve作为Pipeline的元素,则可理解为管道中的一个阀门

先看一下接口定义

public interface Pipeline {
    Valve getBasic();
    void setBasic(Valve var1);
    void addValve(Valve var1);
    Valve[] getValves();
    void removeValve(Valve var1);
    Valve getFirst();
    boolean isAsyncSupported();
    Container getContainer();
    void setContainer(Container var1);
    void findNonAsyncValves(Set<String> var1);
}

public interface Valve {
  public Valve getNext();
  public void setNext(Valve valve);
  public void invoke(Request request, Response response)
}

Pipeline的初始化

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

每一个容器都有一个pipeline属性,在容器的父类ContainerBase中实现

addValve

在pipeline接口中,提供了addValve方法,通过addValve,可以在管道中增加阀门

  public void addValve(Valve valve) {
		......
        // Add this Valve to the set associated with this Pipeline
        if (first == null) {
            first = valve;
            valve.setNext(basic);
        } else {
            Valve current = first;
            while (current != null) {
                if (current.getNext() == basic) {
                    current.setNext(valve);
                    valve.setNext(basic);
                    break;
                }
                current = current.getNext();
            }
        }
    }

Valve.invoke()会按照addValue()的顺序执行,Valve之间的调用通过getNext().invoke(request, response)执行

setBasic

在pipeline接口中,setBasic设置用于设置基础阀门,需要注意每一个容器都定义了BasicValve,在执行时,BasicValve总是最后执行的

StandardEngineValve调用下一管道的过程

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

        // Select the Host to be used for this Request
        Host host = request.getHost();
        ......
        host.getPipeline().getFirst().invoke(request, response);

    }

可以看到当前容器的Pipeline对象中 first Valve是ErrorReportValve,同时起next属性指向的是StandardHostValve那么在ErrorReportValve.invoke()中调用getNext().invoke(request, response)则会执行StandardHostValve.invoke()

StandardWrapperValve

StandardWrapperValue是Wrapper的标准阀,也是Pipeline中的最后一道阀门,也是触发Filter,执行Servlet的地方。看了这么久终于回到开头的Filter了!!!
下面看看StandardWrapperValve.invoke方法

第一个核心的点是通过构建ApplicationFilterChain执行过滤器

createFilterChain方法的职责是构造过滤器链

 public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
     ......
            filterChain.setServlet(servlet);
	.....
                String servletName = wrapper.getName();

// 类型和路径都匹配的情况下,将context.filterConfig放到过滤器链里面
                int i;
                ApplicationFilterConfig filterConfig;
                for(i = 0; i < filterMaps.length; ++i) {
                    if (matchDispatcher(filterMaps[i], dispatcher) && matchFiltersURL(filterMaps[i], requestPath)) {
                        filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());
                        if (filterConfig != null) {
                            filterChain.addFilter(filterConfig);
                        }
                    }
                }
// 类型和ServletName都匹配的情况下,将context.filterConfig放到过滤器链里面
                for(i = 0; i < filterMaps.length; ++i) {
                    if (matchDispatcher(filterMaps[i], dispatcher) && matchFiltersServlet(filterMaps[i], servletName)) {
                        filterConfig = (ApplicationFilterConfig)context.findFilterConfig(filterMaps[i].getFilterName());
                        if (filterConfig != null) {
                            filterChain.addFilter(filterConfig);
                        }
                    }
                }

                return filterChain;
            } else {
                return filterChain;
            }
        }
    }

doFilter的执行

doFilter的执行是一个递归的过程,执行chain.doFilter()时会执行ApplicationFilterChain 中的以下逻辑

private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.pos < this.n) {
            ApplicationFilterConfig filterConfig = this.filters[this.pos++];

            try {
                Filter filter = filterConfig.getFilter();
                if (request.isAsyncSupported() && "false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {
                    request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
                }

                if (Globals.IS_SECURITY_ENABLED) {
                    Principal principal = ((HttpServletRequest)request).getUserPrincipal();
                    Object[] args = new Object[]{request, response, this};
                    SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);
                } else {
                    // 执行Filter
                    filter.doFilter(request, response, this);
                }

            ......异常处理
        } else {
            try {
                if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
                    lastServicedRequest.set(request);
                    lastServicedResponse.set(response);
                }

                if (request.isAsyncSupported() && !this.servletSupportsAsync) {
                    request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", Boolean.FALSE);
                }

                if (request instanceof HttpServletRequest && response instanceof HttpServletResponse && Globals.IS_SECURITY_ENABLED) {
                    Principal principal = ((HttpServletRequest)request).getUserPrincipal();
                    Object[] args = new Object[]{request, response};
                    SecurityUtil.doAsPrivilege("service", this.servlet, classTypeUsedInService, args, principal);
                } else {
                    // 执行servlet
                    this.servlet.service(request, response);
                }
            ......异常处理、释放资源

        }
    }           

this.pos < this.n 通过Filter的下标索引,判断是否Filter执行完成

filter.doFilter(request, response, this)执行Filter,注意这里会传入ApplicationFilterChain对象本身,在具体的Filter中会调用chain.doFilter执行下一个Filter,再通过pos++获取下一个Filter,最终构成了链式结构

③ 执行完成所有Filter后通过this.servlet.service(request, response)执行Servlet,此时请求会先到达HttpServlet的doGet或doPost方法,最后请求到达DispatcherServlet,由DispatcherServlet根据路径将请求映射到Controller,整个过程大致是: Wrapper -> Filter -> DispatcherServlet -> Controller

总结

至此,通过分析Tomcat连接器与容器,完成了一个Web请求到应用服务器Controller的过程分析,那么现在再回头看看开篇的问题,是否对Filter有更多的认识了呢?有时候一些看起来简单的东西,背后的原理却值得我们思考

知识就像浩瀚的海洋,我们往往只看到冰山一角
学无止境!与君共勉

PS:看到这里的朋友们,帮忙点个赞呀!