首页 > 代码库 > Tomcat源码解读:我们发起的HTTP请求如何到达Servlet的

Tomcat源码解读:我们发起的HTTP请求如何到达Servlet的

在上一节中,了解了Tomcat服务器启动的整个过程,现在来了解一下Tomcat如何接收到HTTP请求,并将请求送达至Servlet,Servlet处理后,响应信息又是如何返回给浏览器的呢?这两个问题是接下来要研究的课题,本节先研究第一个问题。

    了解一点点网络知识的人,都会知道TCP连接通信是基于Socket的,上一节也有提到这点。通过上一节的说明,可以了解到Tomcat服务器在内部已经使用Endpoint类封装了Socket。

 本篇会包含大量的源码解读说,由于篇幅原因,就将源码折叠起来,如果想了解其过程,简易打开折叠的源码看看。

猜想流程

在查看源码之前,建议大家自己想一想,如果是你来设计,会怎么处理请求。我先自己想了一下请求处理过程:

 

1) ServerSocket accept客户端的请求得到Socket对象,这一部分肯定要与Connector关联起来,因为只有Connector上配置了与TCP相关的东西,例如:port, protocol等。

2) Tomcat中的某个组件(在Connector范围内)解析Socket对象,封装成一个Request对象

3) Tomcat中某个组件(在Connector范围内)根据Request对象在服务器上找出于这个连接器关联的Container,也就是Engine。因为Connector和Engine都是Service范围内的,并且一个Service内可以有多个Connector,只能有一个Engine,所以在Connector确定的情况下,Engine就是确定的。接下来只需要找到所在的虚拟主机Host就行了。

4) 找到请求所属的主机Host,根据HTTP请求的URL就可以了。这是因为URL由遵循下面的结构:Protocol://host:port/context/path

5)找到请求所属的context,也就是说找到请求那个web app的。

6)根据请求的path部分找到所在的Web app的资源处理Servlet

因为在web.xml中配置了Servlet的url-pattern,也就是那个Servlet处理哪些路径下的资源请求。

7)如果有filter,先filter处理。

8)调用Servlet#doService()方法。这是在做Java Web开发时了解到的。然后根据请求的method(GET、POST、PUT等),自动的解析为doXxx(doGet, doPost)

 

简易类图

在了解这一部分前,我简单的看了一下源码,做了一个简单的类图:

 

 

 

接下来,就通过调试说明这个处理过程:

调试前,先看看系统中已有的线程:

 

从这个线程列表里可以看出来,这些线程都是各个Endpoint内部的属性(Acceptor、CometPoller、Poller)的线程。所以我们发送的请求肯定是这几类线程中的一个来处理的。

 

 

真实流程解读

1)使用AprEndpoint$Acceptor#run()来接收TCP连接

 

通过类图知道,在AprEndpoint内部有很多的Acceptor,应该是用于接收不同端口的TCP连接的吧。

 

猜想不能解决问题,经过调试,确实发现这里开始接收TCP连接请求了:

public void run() {             // Loop until we receive a shutdown command            while (running) {                 // Loop if endpoint is paused                while (paused && running) {                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                        // Ignore                    }                }                 if (!running) {                    break;                }                try {                    // Accept the next incoming connection from the server socket                    long socket = Socket.accept(serverSock);                    /*                     * In the case of a deferred accept unlockAccept needs to                     * send data. This data will be rubbish, so destroy the                     * socket and don‘t process it.                     */                    if (deferAccept && (paused || !running)) {                        destroySocket(socket);                        continue;                    }                    // Hand this socket off to an appropriate processor                    if (!processSocketWithOptions(socket)) {                        // Close socket and pool right away                        destroySocket(socket);                    }                } catch (Throwable t) {                    if (running) {                        String msg = sm.getString("endpoint.accept.fail");                        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);                        }                    }                }                 // The processor will recycle itself when it finishes             }         }
View Code

 

这里附加一些Java的基本知识:它只用接收请求就行了,别的什么也不用做。

它并没有拿到一个Socket,也没有将socket传递给其他的类,但是却能够完成请求的处理,这是为什么呢?

上面的代码中Socket.acccpt(serverSocket),就是接收一个Socket请求,标示是serverSocket 。

 

    因为使用了内部类,这样的好处是内部类方法和属性对于外部类是可见的,外部类的方法属性对内部类也是可见的。其实内部类的方法可以认为是对于外部类的扩充,只是在其他的类中不能使用这些方法而已,只能在外部类,内部类本身里使用而已(当然了,如果内部类是public,方法也是public情况下,第三方类还是可以使用的)。

 

 

2)使用AprEndpoint$Worker#run()来做启动TCP连接处理流程

 

public void run() {             // Process requests until we receive a shutdown signal            while (running) {                 // Wait for the next socket to be assigned// 拿到Acceptor接收到的请求                long socket = await();                if (socket == 0)                    continue;                 if (!deferAccept && options) {                    if (setSocketOptions(socket)) {                        getPoller().add(socket);                    } else {                        // Close socket and pool                        destroySocket(socket);                        socket = 0;                    }                } else {                     // Process the request from this socket                    if ((status != null) && (handler.event(socket, status) == Handler.SocketState.CLOSED)) {                        // Close socket and pool                        destroySocket(socket);                        socket = 0;// 调用Handler.process处理Socket// 在Handler对象内部,会找到一个HTTP11AprProcessor处理器,用于处理Socket请求// 然后在HTTP11AprProcessor处理过程中,又会转给                    } else if ((status == null) && ((options && !setSocketOptions(socket))                            || handler.process(socket) == Handler.SocketState.CLOSED)) {                        // Close socket and pool                        destroySocket(socket);                        socket = 0;                    }                }                 // Finish up this request                recycleWorkerThread(this);             }         }
View Code

 

3)根据Socket解析HTTP Header封装成Request

 由于方法比较长,就不粘了,只粘出主要代码:

inputBuffer.setSocket(socket);inputBuffer.parseHeaders();

而inputBuffer#parseHeader(): 

/**     * Parse the HTTP headers.     */    public void parseHeaders()        throws IOException {// 每一次调用parseHeader(),就是解析HTTP Header中的一条。 这个是基于HTTP Header的格式来解析的,这个不明白,可以先了解一下HTTP协议while (parseHeader()) {        }        parsingHeader = false;        end = pos;    }

InputBuffer# parseHeader(),这个过程比较复杂,就不贴了。下面贴出来解析后的消息头: 

 

 

4)根据消息头解析出Container相关信息

准备Request,其实就是做一些额外的处理,例如根据消息头解析host, port,context,path等等,将其封装为StandardContext对象,然后放在Request对象里。

 

5)调用adapter#service(),请求转给Container(Engine)

 

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) {             // Create objects            request = (Request) connector.createRequest();            request.setCoyoteRequest(req);            response = (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().setQueryStringEncoding                (connector.getURIEncoding());         }         if (connector.getXpoweredBy()) {            response.addHeader("X-Powered-By", POWERED_BY);        }         boolean comet = false;         try {             // Parse and set Catalina and configuration specific            // request parameters            req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());            if (postParseRequest(req, request, res, response)) {                // Calling the container// 通过这一步,获取到与Connector关联的Container【也就是Engine】,如此就将流程转给了Tomcat容器处理了。//这也是这个CoyoteApapter的作用// 这里connector.getContainer()得到的是一个Engine// engine.getPipeline().getFirst()得到的其实是Engine的pipeliene中的一个StandardEngineValue。Tomcat中的Value是用于执行一些任务的。至于为什么起名为Value,就不太清楚了。只需要知道这段代码执行的是StandardEngineValue#invoke()就可以了。                 connector.getContainer().getPipeline().getFirst().invoke(request, response);                 if (request.isComet()) {                    if (!response.isClosed() && !response.isError()) {                        if (request.getAvailable() || (request.getContentLength() > 0 && (!request.isParametersParsed()))) {                            // Invoke a read event right away if there are available bytes                            if (event(req, res, SocketStatus.OPEN)) {                                comet = true;                                res.action(ActionCode.ACTION_COMET_BEGIN, null);                            }                        } else {                            comet = true;                            res.action(ActionCode.ACTION_COMET_BEGIN, null);                        }                    } else {                        // Clear the filter chain, as otherwise it will not be reset elsewhere                        // since this is a Comet request                        request.setFilterChain(null);                    }                }             }             if (!comet) {                response.finishResponse();                req.action(ActionCode.ACTION_POST_REQUEST , null);            }         } catch (IOException e) {            ;        } finally {            req.getRequestProcessor().setWorkerThreadName(null);            // Recycle the wrapper request and response            if (!comet) {                request.recycle();                response.recycle();            } else {                // Clear converters so that the minimum amount of memory                // is used by this processor                request.clearEncoders();                response.clearEncoders();            }        }     } 
View Code

 

6)在Engine》Host》Context之间通过Pipeline传递请求

6.1)StandarEngineValue#invoke()从将Request转给Host处理

下面是StandardEngineValue#invoke()的源码:

/**     * Select the appropriate child Host to process this request,     * based on the requested server name.  If no matching Host can     * be found, return an appropriate HTTP error.     *     */    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) {            response.sendError                (HttpServletResponse.SC_BAD_REQUEST,                 sm.getString("standardEngine.noHost",                              request.getServerName()));            return;        }         // Ask this Host to process this request// 这个设计与之前的设计思路是一致的,调用的是StandardHostValue#invoke()        host.getPipeline().getFirst().invoke(request, response);     }
View Code

从invoke方法的注释就可以知道,是要从engine范围内,根据第3)步找到的HOST信息,将请求交给host处理。

 

6.2)StandardHostValue#invoke()将Request转给Context处理

这一步与上面是类似的,将请求交给了StandardContextValue处理。

/**     * Select the appropriate child Context to process this request,     * based on the specified request URI.  If no matching Context can     * be found, return an appropriate HTTP error.     */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) {            response.sendError                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,                 sm.getString("standardHost.noContext"));            return;        }         // Bind the context CL to the current thread        if( context.getLoader() != null ) {            // Not started - it should check for availability first            // This should eventually move to Engine, it‘s generic.            Thread.currentThread().setContextClassLoader                    (context.getLoader().getClassLoader());        }         // Ask this Context to process this request        context.getPipeline().getFirst().invoke(request, response);         // Access a session (if present) to update last accessed time, based on a        // strict interpretation of the specification        if (Globals.STRICT_SERVLET_COMPLIANCE) {            request.getSession(false);        }         // Error page processing        response.setSuspended(false);         Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);         if (t != null) {            throwable(request, response, t);        } else {            status(request, response);        }         // Restore the context classloader        Thread.currentThread().setContextClassLoader            (StandardHostValve.class.getClassLoader());     }
View Code

至此,请求终于到达处理它的WEB应用程序了。 

 

7)启动监听器处理,接着在Context找到Wrapper,让Wrapper处理

/**     * Select the appropriate child Wrapper to process this request,     * based on the specified request URI.  If no matching Wrapper can     * be found, return an appropriate HTTP error.     *     * @param request Request to be processed     * @param response Response to be produced     * @param valveContext Valve context used to forward to the next Valve     *     * @exception IOException if an input/output error occurred     * @exception ServletException if a servlet error occurred     */    public final void invoke(Request request, Response response)        throws IOException, ServletException {         // Disallow any direct access to resources under WEB-INF or META-INF// META-INF 和WEB-INF目录是应用程序内部的专属空间,是不允许直接这两个目录下的内容的。所以如果你的请求URL上包含着两个目录,都不会被处理。// 在应用程序内部,例如Servlet里,可以处理这两个目录下的文件。譬如JSP文件放在WEB-INF目录下,直接访问是不可见的,但是通过Servlet进行forward就可以。但是这都是请求到达Servlet之后的事了,现在请求还没到Servlet呢,别急。        MessageBytes requestPathMB = request.getRequestPathMB();        if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))            || (requestPathMB.equalsIgnoreCase("/META-INF"))            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))            || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {            notFound(response);            return;        }         // Wait if we are reloading// 软重启应用程序,前提是Web应用程序下有资源改变,一般情况下,不会重启的。        boolean reloaded = false;        while (context.getPaused()) {            reloaded = true;            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                ;            }        }         // Reloading will have stopped the old webappclassloader and        // created a new one// 要是重启过,就得重新设置类加载器        if (reloaded &&                context.getLoader() != null &&                context.getLoader().getClassLoader() != null) {            Thread.currentThread().setContextClassLoader(                    context.getLoader().getClassLoader());        }         // Select the Wrapper to be used for this Request        Wrapper wrapper = request.getWrapper();        if (wrapper == null) {            notFound(response);            return;        } else if (wrapper.isUnavailable()) {            // May be as a result of a reload, try and find the new wrapper            wrapper = (Wrapper) container.findChild(wrapper.getName());            if (wrapper == null) {                notFound(response);                return;            }        }         // Normal request processing// 取得所有的监听器,这些监听器都是我们在web.xml中配置的(有ServletContext(application)、Session、Request 级别的监听器)不管属于哪个级别的,全部查出来。        Object instances[] = context.getApplicationEventListeners();         ServletRequestEvent event = null;         if ((instances != null)                && (instances.length > 0)) {// 封装一个request级别的Event            event = new ServletRequestEvent                (((StandardContext) container).getServletContext(),                 request.getRequest());            // create pre-service event// 轮询前面取到所有的listener,处理RequestListener            for (int i = 0; i < instances.length; i++) {                if (instances[i] == null)                    continue;                if (!(instances[i] instanceof ServletRequestListener))                    continue;                ServletRequestListener listener =                    (ServletRequestListener) instances[i];                try {// listener处理                    listener.requestInitialized(event);                } catch (Throwable t) {                    container.getLogger().error(sm.getString("standardContext.requestListener.requestInit",                                     instances[i].getClass().getName()), t);                    ServletRequest sreq = request.getRequest();                    sreq.setAttribute(Globals.EXCEPTION_ATTR,t);                    return;                }            }        }// 同之前的设计一样,找到StandardWrapperValue,处理Request        wrapper.getPipeline().getFirst().invoke(request, response); // 监听器结束生命周期        if ((instances !=null ) &&                (instances.length > 0)) {            // create post-service event            for (int i = 0; i < instances.length; i++) {                if (instances[i] == null)                    continue;                if (!(instances[i] instanceof ServletRequestListener))                    continue;                ServletRequestListener listener =                    (ServletRequestListener) instances[i];                try {                    listener.requestDestroyed(event);                } catch (Throwable t) {                    container.getLogger().error(sm.getString("standardContext.requestListener.requestDestroy",                                     instances[i].getClass().getName()), t);                    ServletRequest sreq = request.getRequest();                    sreq.setAttribute(Globals.EXCEPTION_ATTR,t);                }            }        }                    }
View Code

 

8)Servlet处理请求

请求并不是直接就让Servlet处理的,这点做过Web开发的人都知道,至少中间还有个Filter要处理吧。

下面是StandardWrapperValue#invoke()的源码,就来了解一下它是咋处理的。

这点代码包含了很多内容,解析来会一一说明:

/**     * Invoke the servlet we are managing, respecting the rules regarding     * servlet lifecycle and SingleThreadModel support.     *     */    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++;        StandardWrapper wrapper = (StandardWrapper) getContainer();        Servlet servlet = null;        Context context = (Context) wrapper.getParent();               // 检查context是否可用,就是检查web应用程序是否可用,因为可能出现应用程序挂了,或者软重启了        if (!context.getAvailable()) {        response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                           sm.getString("standardContext.isUnavailable"));            unavailable = true;        }         // 检查要处理请求的Servlet是否可用,如果Servlet被删除,然后也重启Context了,servlet就没有了,所以又必要检查一下。        if (!unavailable && wrapper.isUnavailable()) {            container.getLogger().info(sm.getString("standardWrapper.isUnavailable",                    wrapper.getName()));            long available = wrapper.getAvailable();            if ((available > 0L) && (available < Long.MAX_VALUE)) {                response.setDateHeader("Retry-After", available);                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                        sm.getString("standardWrapper.isUnavailable",                                wrapper.getName()));            } else if (available == Long.MAX_VALUE) {                response.sendError(HttpServletResponse.SC_NOT_FOUND,                        sm.getString("standardWrapper.notFound",                                wrapper.getName()));            }            unavailable = true;        }         // 分配一个Servlet对象来处理请求,下面8.1会详细说明如何分配的        try {            if (!unavailable) {                servlet = wrapper.allocate();            }        } catch (UnavailableException e) {            container.getLogger().error(                    sm.getString("standardWrapper.allocateException",                            wrapper.getName()), e);            long available = wrapper.getAvailable();            if ((available > 0L) && (available < Long.MAX_VALUE)) {              response.setDateHeader("Retry-After", available);              response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                           sm.getString("standardWrapper.isUnavailable",                                        wrapper.getName()));            } else if (available == Long.MAX_VALUE) {              response.sendError(HttpServletResponse.SC_NOT_FOUND,                           sm.getString("standardWrapper.notFound",                                        wrapper.getName()));            }        } catch (ServletException e) {            container.getLogger().error(sm.getString("standardWrapper.allocateException",                             wrapper.getName()), StandardWrapper.getRootCause(e));            throwable = e;            exception(request, response, e);            servlet = null;        } catch (Throwable e) {            container.getLogger().error(sm.getString("standardWrapper.allocateException",                             wrapper.getName()), e);            throwable = e;            exception(request, response, e);            servlet = null;        }         // Identify if the request is Comet related now that the servlet has been allocated        boolean comet = false;        if (servlet instanceof CometProcessor                && request.getAttribute("org.apache.tomcat.comet.support") == Boolean.TRUE) {            comet = true;            request.setComet(true);        }               // 告诉Connector:已经拿到处理的Servlet了。        try {            response.sendAcknowledgement();        } catch (IOException e) {        request.removeAttribute(Globals.JSP_FILE_ATTR);            container.getLogger().warn(sm.getString("standardWrapper.acknowledgeException",                             wrapper.getName()), e);            throwable = e;            exception(request, response, e);        } catch (Throwable e) {            container.getLogger().error(sm.getString("standardWrapper.acknowledgeException",                             wrapper.getName()), e);            throwable = e;            exception(request, response, e);            servlet = null;        }        MessageBytes requestPathMB = null;        if (request != null) {            requestPathMB = request.getRequestPathMB();        }        request.setAttribute            (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,             ApplicationFilterFactory.REQUEST_INTEGER);        request.setAttribute            (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,             requestPathMB);        // 为request创建Filter链,是创建,是为每一个请求创建过滤器链,不是获取已有的。        ApplicationFilterFactory factory =            ApplicationFilterFactory.getInstance();        ApplicationFilterChain filterChain =            factory.createFilterChain(request, wrapper, servlet);        // Reset comet flag value after creating the filter chain        request.setComet(false);         //调用filter chain处理请求,这个过程我还有一篇文章专门讲述          try {            String jspFile = wrapper.getJspFile();            if (jspFile != null)              request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);            else              request.removeAttribute(Globals.JSP_FILE_ATTR);            if ((servlet != null) && (filterChain != null)) {                // Swallow output if needed                if (context.getSwallowOutput()) {                    try {                        SystemLogHandler.startCapture();                        if (comet) {                            filterChain.doFilterEvent(request.getEvent());                            request.setComet(true);                        } else {                            filterChain.doFilter(request.getRequest(),                                    response.getResponse());                        }                    } finally {                        String log = SystemLogHandler.stopCapture();                        if (log != null && log.length() > 0) {                            context.getLogger().info(log);                        }                    }                } else {                    if (comet) {                        request.setComet(true);                        filterChain.doFilterEvent(request.getEvent());                    } else {                        filterChain.doFilter                            (request.getRequest(), response.getResponse());                    }                }             }            request.removeAttribute(Globals.JSP_FILE_ATTR);        } catch (ClientAbortException e) {        request.removeAttribute(Globals.JSP_FILE_ATTR);            throwable = e;            exception(request, response, e);        } catch (IOException e) {        request.removeAttribute(Globals.JSP_FILE_ATTR);            container.getLogger().error(sm.getString("standardWrapper.serviceException",                             wrapper.getName()), e);            throwable = e;            exception(request, response, e);        } catch (UnavailableException e) {        request.removeAttribute(Globals.JSP_FILE_ATTR);            container.getLogger().error(sm.getString("standardWrapper.serviceException",                             wrapper.getName()), e);            //            throwable = e;            //            exception(request, response, e);            wrapper.unavailable(e);            long available = wrapper.getAvailable();            if ((available > 0L) && (available < Long.MAX_VALUE)) {                response.setDateHeader("Retry-After", available);                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                           sm.getString("standardWrapper.isUnavailable",                                        wrapper.getName()));            } else if (available == Long.MAX_VALUE) {              response.sendError(HttpServletResponse.SC_NOT_FOUND,                            sm.getString("standardWrapper.notFound",                                        wrapper.getName()));            }            // Do not save exception in ‘throwable‘, because we            // do not want to do exception(request, response, e) processing        } catch (ServletException e) {        request.removeAttribute(Globals.JSP_FILE_ATTR);            Throwable rootCause = StandardWrapper.getRootCause(e);            if (!(rootCause instanceof ClientAbortException)) {                container.getLogger().error(sm.getString("standardWrapper.serviceException",                                 wrapper.getName()), rootCause);            }            throwable = e;            exception(request, response, e);        } catch (Throwable e) {            request.removeAttribute(Globals.JSP_FILE_ATTR);            container.getLogger().error(sm.getString("standardWrapper.serviceException",                             wrapper.getName()), e);            throwable = e;            exception(request, response, e);        }         // 释放过滤器链,此时Servlet已经执行完毕。        if (filterChain != null) {            if (request.isComet()) {                // If this is a Comet request, then the same chain will be used for the                // processing of all subsequent events.                filterChain.reuse();            } else {                filterChain.release();            }        }         // 回收servlet        try {            if (servlet != null) {                wrapper.deallocate(servlet);            }        } catch (Throwable e) {            container.getLogger().error(sm.getString("standardWrapper.deallocateException",                             wrapper.getName()), e);            if (throwable == null) {                throwable = e;                exception(request, response, 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) {            container.getLogger().error(sm.getString("standardWrapper.unloadException",                             wrapper.getName()), e);            if (throwable == null) {                throwable = e;                exception(request, response, e);            }        }        long t2=System.currentTimeMillis();         long time=t2-t1;        processingTime += time;        if( time > maxTime) maxTime=time;        if( time < minTime) minTime=time;     } 
View Code

 

  

8.1)分配Servlet

要让Servlet处理请求,得先分配Servlet,分配Servlet对象也是有讲究的,因为Servlet有两种运行模式,单线程运行模式和多线程运行模式。

 

public Servlet allocate() throws ServletException {         // If we are currently unloading this servlet, throw an exception        if (unloading)            throw new ServletException              (sm.getString("standardWrapper.unloading", getName()));         boolean newInstance = false;               // If not SingleThreadedModel, return the same instance every time        if (!singleThreadModel) {             // 如果是第一次请求这个Servlet,Servlet肯定还没有创建,这时就要创建一个Servlet实例,并初始化。这一点,我想有的面试官会问到的。            if (instance == null) {                synchronized (this) {                    if (instance == null) {                        try {                            if (log.isDebugEnabled())                                log.debug("Allocating non-STM instance");// loadServlet()过程也做了很多事:// 如果是jsp请求:解析JSP成一个Servlet类,编译,加载(解析,编译过程只在第一次请求该JSP文件时进行)// 如果是html,img,css,js等就返回DefaultServlet                             instance = loadServlet();                            // For non-STM, increment here to prevent a race                            // condition with unload. Bug 43683, test case #3                            if (!singleThreadModel) {                                newInstance = true;                                countAllocated.incrementAndGet();                            }                        } catch (ServletException e) {                            throw e;                        } catch (Throwable e) {                            throw new ServletException                                (sm.getString("standardWrapper.allocate"), e);                        }                    }                }            }// 返回Servlet实例。            if (!singleThreadModel) {                if (log.isTraceEnabled())                    log.trace("  Returning non-STM instance");                // For new instances, count will have been incremented at the                // time of creation                if (!newInstance) {                    countAllocated.incrementAndGet();                }                return (instance);            }        } // 要是单线程模式下运行的Sevlet,就得等Servlet执行完毕,被回收后,再分配给你这个请求        synchronized (instancePool) {             while (countAllocated.get() >= nInstances) {                // Allocate a new instance if possible, or else wait                if (nInstances < maxInstances) {                    try {                        instancePool.push(loadServlet());                        nInstances++;                    } catch (ServletException e) {                        throw e;                    } catch (Throwable e) {                        throw new ServletException                            (sm.getString("standardWrapper.allocate"), e);                    }                } else {                    try {                        instancePool.wait();                    } catch (InterruptedException e) {                        ;                    }                }            }            if (log.isTraceEnabled())                log.trace("  Returning allocated STM instance");            countAllocated.incrementAndGet();            return (Servlet) instancePool.pop();         }     }
View Code

  

8.2)过滤器链处理

 

参考博客:Filter

 

8.3)Servlet.service() ,Servlet执行

过滤器对象里有个属性就是servelt,在过滤器链处理完毕,就直接调用了Servlet了。

 

官方的说法

  

 

官方说法中前三步中涉及到的类,可能有我调试时不同,这是因为采用的协议不同,协议不同,与protocol相关的processor也就不同。但是整个流程就是这个样子的。

 

Tomcat源码解读:我们发起的HTTP请求如何到达Servlet的