Tomcat源码分析:处理servlet请求

Tomcat源码目录下的webapps中包含有一些简单的servlet例子,在此通过分析hello worldservlet请求过程来看tomcat是如何执行的。

启动tomcat后,访问http://localhost:8080/examples/servlets/servlet/HelloWorldExample,以debug模式单步调试,可以得到流程图。

启动过程主要分为3个阶段:

1、tomcat如何捕获servlet请求。

2、接收到请求后如何在各容器中流动,最后到达最终处理地点。

3、处理完成后如何返回。

启动过程主要分为3个阶段:

1、tomcat如何捕获servlet请求。

2、接收到请求后如何在各容器中流动,最后到达最终处理地点。

3、处理完成后如何返回。

 下面逐一进行分析。


1、第一阶段:捕获请求

1) JIoEndpoint的内部类Acceptor,接受浏览器的socket请求。

protected class Acceptor extends AbstractEndpoint.Acceptor {
        @Override
        public void run() {
            //...
        	while (running) {
               //...
                    try {
                    	// 接受发送过来的请求
                        socket = serverSocketFactory.acceptSocket(serverSocket);
                    } catch (IOException ioe) {
                       //...
                    }
                    //...
                    if (!processSocket(socket)) {
                        countDownConnection();
                        //关闭连接
                        closeSocket(socket);
                    }
                   //...
            }
            //...
        }
    }

2)调用SocketProcessor processSocket方法处理请求,请求会被传到AbstractProtocol内部类AbstractConnectionHandlerprocess方法去处理。此方法中会创建Http11Processor的对象processor

3) 使用创建的processorprocess方法来处理请求。此方法中将请求解析成RequestResponse对象。又会调用CoyoteAdapter来进行适配处理,org.apache.coyote.Request对象被转成了org.apache.catalina.connector.Request对象,后一类型的对象才是在Tomcat容器流转时真正传递的对象。

postParseRequest方法中,通过map方法的设置request的成员变量mappingData的成员变量hostcontextwarp信息,接着从mappingData中取出contextwrapper,直接设置到request对象的成员变量contextwrapper中。

Tomcat容器启动过程中会将用到的HostContextWrapper组件维护到与Connector相关的Mapper对象里,这样容器在接收到请求之后,才可以根据请求的URL等信息匹配到具体的hostcontextwrapper

方法的最后,将org.apache.catalina.connector.Request对象和

org.apache.catalina.connector.Response对象,交给StandardEngine 做核心的处理工作了。

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


2、第二阶段:在各容器中流动

Request在各个容器里面的穿梭大致是这样一种方式:  

每个容器里面都有一个管道(pipline), 专门用来传送Request用的。管道里面又有好几个阀门(valve), 专门用来过滤Request用的。  

在管道的低部通常都会放上一个默认的阀们。 这个阀们至少会做一件事情,就是把Request交给子容器。 

1)StandardEngine pipeline里面放的是:StandardEnginValve。设置是否支持异步请求后,直接将请求交由StandardHostpipeline去处理。

public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        Host host = request.getHost();
        if (host == null) {
            //...
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }
        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);
    }

2) StandardHost pipline里面放的第一个valve是:AccessLogValve

public void invoke(Request request, Response response) throws IOException,
            ServletException {
        getNext().invoke(request, response);
    }    

此方法中直接调用下一个valveErrorReportValveinvoke方法,其中又调用下一个valveStandardHostValveinvoke方法。StandardHostValveinvoke方法中,主要工作是:

 根据Request来确定哪个Context来处理(Context其实就是webapp 比如http://localhost:8080/examples/servlets/servlet/HelloWorldExample这里examples就是Context!),并把这个Contextclassloader赋值给当前线程,然后转到contextpipeline。这样request就只看得见当前的context下面的classesjar 而看不见tomcat服务本身的类, EngineValve等。

public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        // Select the Context to be used for this Request
        Context context = request.getContext();
        if (context == null) {
           //...
        }
        // Bind the context CL to the current thread
        if( context.getLoader() != null ) {
             if (Globals.IS_SECURITY_ENABLED) {
                //...              
            } else {
                Thread.currentThread().setContextClassLoader
                        (context.getLoader().getClassLoader());
            }
        }
        //...
 if (!asyncAtStart || asyncDispatching) {
context.getPipeline().getFirst().invoke(request, response);//转context的pipeline
}
//...
    }

3) contextpipeline中的第一个valveFormAuthenticator,验证或更新session等操作之后,转到StandardContextValve,此方法中将request交给子容器StandardWrapperpipeline

4) Wrapperpipeline的第一个valveStandardWrapperValve。主要工作是:1、按类名HelloWorldExample,创建一个HttpServlet实例,用于处理请求。2、创建filter chain3filterChain.doFilter(request,response),请求通过一系列filter之后,调用HttpServlet.service(request, response),此方法中根据http请求的方法判断调用具体类(此例中为HelloWorldExample)中的doPost,doPut,doGet等。


3、第三阶段:处理完成后续工作

1)ApplicationFilterChain重置,释放分配的servlet实例

2)重置StandardHostclassloader

Thread.currentThread().setContextClassLoader(StandardHostValve.class.getClassLoader());

3) 如果有错误,处理错误信息。

4) 打印日志。


网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者何欢授权发布。