浅读Tomcat源码(三)---请求处理
来源:互联网 发布:交通安全事故数据 编辑:程序博客网 时间:2024/05/16 11:48
在上一篇中,我们讲述了Tomcat在启动之初所做的事情,我们简单罗列回顾一下
1)依次开启组件,其中较为核心的是开启Connector的serverSocket,并等待请求
2)开新线程初始化容器,补充一点,之所以要开启新线程而不在主线程完成,个人理解核心原因是为了在Tomcat运行过程中可以直接添加新的应用
3)开启等待关闭线程,等待Tomcat的关闭指令
这一篇我们将着重讲解Tomcat在接收到新的Http请求的时候应该做的事情
首先我们明白一点,Http协议是一种基于TCP连接的应用层协议,而Socket又是基于TCP的一种横跨网络层、传输层、应用层的接口,而在Tomcat中完成网络连接的代码也基本都是由Socket实现的。
在上一篇中我们看到了JIoEndpoint中开启了一个监听Http连接端口(默认8080)的serverSocket,当有请求的时候,serverSocket会接收到一个socket,并将socket传入processSocket方法中处理:
- Socket socket = null;
- try {
- // Accept the next incoming connection from the server
- // socket
- socket = serverSocketFactory.acceptSocket(serverSocket);
- } catch (IOException ioe) {
- // Introduce delay if necessary
- errorDelay = handleExceptionWithDelay(errorDelay);
- // re-throw
- throw ioe;
- }
- // Successful accept, reset the error delay
- errorDelay = 0;
- // Configure the socket
- if (setSocketOptions(socket)) {
- // Hand this socket off to an appropriate processor
- if (!processSocket(socket)) {
- // Close socket right away
- try {
- socket.close();
- } catch (IOException e) {
- // Ignore
- }
- } else {
- countUpConnection();
- }
- } else {
- // Close socket right away
- try {
- socket.close();
- } catch (IOException e) {
- // Ignore
- }
- }
- /**
- * Process a new connection from a new client. Wraps the socket so
- * keep-alive and other attributes can be tracked and then passes the socket
- * to the executor for processing.
- *
- * @param socket The socket associated with the client.
- *
- * @return <code>true</code> if the socket is passed to the
- * executor, <code>false</code> if something went wrong or
- * if the endpoint is shutting down. Returning
- * <code>false</code> is an indication to close the socket
- * immediately.
- */
- protected boolean processSocket(Socket socket) {
- // Process the request from this socket
- try {
- SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
- wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
- // During shutdown, executor may be null - avoid NPE
- if (!running) {
- return false;
- }
- getExecutor().execute(new SocketProcessor(wrapper));
- } catch (RejectedExecutionException x) {
- log.warn("Socket processing request was rejected for:"+socket,x);
- 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
- log.error(sm.getString("endpoint.process.fail"), t);
- return false;
- }
- return true;
- }
- /**
- * This class is the equivalent of the Worker, but will simply use in an
- * external Executor thread pool.
- */
- protected class SocketProcessor implements Runnable {
- protected SocketWrapper<Socket> socket = null;
- protected SocketStatus status = null;
- public SocketProcessor(SocketWrapper<Socket> socket) {
- if (socket==null) throw new NullPointerException();
- this.socket = socket;
- }
- public SocketProcessor(SocketWrapper<Socket> socket, SocketStatus status) {
- this(socket);
- this.status = status;
- }
- @Override
- public void run() {
- boolean launch = false;
- synchronized (socket) {
- try {
- SocketState state = SocketState.OPEN;
- try {
- // SSL handshake
- serverSocketFactory.handshake(socket.getSocket());
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("endpoint.err.handshake"), t);
- }
- // Tell to close the socket
- state = SocketState.CLOSED;
- }
- if ( (state != SocketState.CLOSED) ) {
- state = (status==null)?handler.process(socket):handler.process(socket,status);
- }
- if (state == SocketState.CLOSED) {
- // Close socket
- if (log.isTraceEnabled()) {
- log.trace("Closing socket:"+socket);
- }
- countDownConnection();
- try {
- socket.getSocket().close();
- } catch (IOException e) {
- // Ignore
- }
- } else if (state == SocketState.ASYNC_END ||
- state == SocketState.OPEN){
- socket.setKeptAlive(true);
- socket.access();
- launch = true;
- } else if (state == SocketState.LONG) {
- socket.access();
- waitingRequests.add(socket);
- }
- } finally {
- if (launch) {
- try {
- getExecutor().execute(new SocketProcessor(socket, SocketStatus.OPEN));
- } catch (NullPointerException npe) {
- if (running) {
- log.error(sm.getString("endpoint.launch.fail"),
- npe);
- }
- }
- }
- }
- }
- socket = null;
- // Finish up this request
- }
- }
- @Override
- public SocketState process(SocketWrapper<Socket> socket, SocketStatus status) {
- Http11Processor processor = connections.remove(socket);
- try {
- if (processor == null) {
- processor = recycledProcessors.poll();
- }
- if (processor == null) {
- processor = createProcessor();
- }
- if (proto.isSSLEnabled() && (proto.sslImplementation != null)) {
- processor.setSSLSupport(
- proto.sslImplementation.getSSLSupport(
- socket.getSocket()));
- } else {
- processor.setSSLSupport(null);
- }
- SocketState state = socket.isAsync()?processor.asyncDispatch(status):processor.process(socket);
- if (state == SocketState.LONG) {
- connections.put(socket, processor);
- socket.setAsync(true);
- // longPoll may change socket state (e.g. to trigger a
- // complete or dispatch)
- return processor.asyncPostProcess();
- } else {
- socket.setAsync(false);
- recycledProcessors.offer(processor);
- }
- return state;
- } catch(java.net.SocketException e) {
- // SocketExceptions are normal
- log.debug(sm.getString(
- "http11protocol.proto.socketexception.debug"), e);
- } catch (java.io.IOException e) {
- // IOExceptions are normal
- log.debug(sm.getString(
- "http11protocol.proto.ioexception.debug"), e);
- }
- // Future developers: if you discover any other
- // rare-but-nonfatal exceptions, catch them here, and log as
- // above.
- catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- // any other exception or error is odd. Here we log it
- // with "ERROR" level, so it will show up even on
- // less-than-verbose logs.
- log.error(sm.getString("http11protocol.proto.error"), e);
- }
- recycledProcessors.offer(processor);
- return SocketState.CLOSED;
- }
到这里会觉得Tomcat的调用过程怎么那么绕,不过有些经验的开发人员都能意识到这其中深含的模块化思想,目前对Tomcat源码只是初窥门径,我在后面的学习中会整理出Tomcat这些处理过程的模块分化,先继续就着调用的顺序往下看,processor的process过程:
- /**
- * Process pipelined HTTP requests on the specified socket.
- *
- * @param socketWrapper Socket from which the HTTP requests will be read
- * and the HTTP responses will be written.
- *
- * @throws IOException error during an I/O operation
- */
- public SocketState process(SocketWrapper<Socket> socketWrapper)
- throws IOException {
- RequestInfo rp = request.getRequestProcessor();
- rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
- // Set the remote address
- remoteAddr = null;
- remoteHost = null;
- localAddr = null;
- localName = null;
- remotePort = -1;
- localPort = -1;
- // Setting up the I/O
- this.socket = socketWrapper;
- inputBuffer.setInputStream(socket.getSocket().getInputStream());
- outputBuffer.setOutputStream(socket.getSocket().getOutputStream());
- // Error flag
- error = false;
- keepAlive = true;
- int keepAliveLeft = maxKeepAliveRequests>0?socketWrapper.decrementKeepAlive():-1;
- int soTimeout = endpoint.getSoTimeout();
- try {
- socket.getSocket().setSoTimeout(soTimeout);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.debug(sm.getString("http11processor.socket.timeout"), t);
- error = true;
- }
- boolean keptAlive = socketWrapper.isKeptAlive();
- while (!error && keepAlive && !endpoint.isPaused()) {
- // Parsing the request header
- try {
- //TODO - calculate timeout based on length in queue (System.currentTimeMills() - wrapper.getLastAccess() is the time in queue)
- if (keptAlive) {
- if (keepAliveTimeout > 0) {
- socket.getSocket().setSoTimeout(keepAliveTimeout);
- }
- else if (soTimeout > 0) {
- socket.getSocket().setSoTimeout(soTimeout);
- }
- }
- inputBuffer.parseRequestLine(false);
- request.setStartTime(System.currentTimeMillis());
- keptAlive = true;
- if (disableUploadTimeout) {
- socket.getSocket().setSoTimeout(soTimeout);
- } else {
- socket.getSocket().setSoTimeout(connectionUploadTimeout);
- }
- inputBuffer.parseHeaders();
- } catch (IOException e) {
- error = true;
- break;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.header.parse"), t);
- }
- // 400 - Bad Request
- response.setStatus(400);
- adapter.log(request, response, 0);
- error = true;
- }
- if (!error) {
- // Setting up filters, and parse some request headers
- rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
- try {
- prepareRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.request.prepare"), t);
- }
- // 400 - Internal Server Error
- response.setStatus(400);
- adapter.log(request, response, 0);
- error = true;
- }
- }
- if (maxKeepAliveRequests > 0 && keepAliveLeft == 0)
- keepAlive = false;
- // Process the request in the adapter
- if (!error) {
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
- adapter.service(request, response);
- // Handle when the response was committed before a serious
- // error occurred. Throwing a ServletException should both
- // set the status to 500 and set the errorException.
- // If we fail here, then the response is likely already
- // committed, so we can't try and set headers.
- if(keepAlive && !error) { // Avoid checking twice.
- error = response.getErrorException() != null ||
- statusDropsConnection(response.getStatus());
- }
- } catch (InterruptedIOException e) {
- error = true;
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.request.process"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- adapter.log(request, response, 0);
- error = true;
- }
- }
- // Finish the handling of the request
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
- // If we know we are closing the connection, don't drain input.
- // This way uploading a 100GB file doesn't tie up the thread
- // if the servlet has rejected it.
- if(error && !isAsync())
- inputBuffer.setSwallowInput(false);
- if (!isAsync())
- endRequest();
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.request.finish"), t);
- // 500 - Internal Server Error
- response.setStatus(500);
- adapter.log(request, response, 0);
- error = true;
- }
- try {
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("http11processor.response.finish"), t);
- error = true;
- }
- // If there was an error, make sure the request is counted as
- // and error, and update the statistics counter
- if (error) {
- response.setStatus(500);
- }
- request.updateCounters();
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
- // Don't reset the param - we'll see it as ended. Next request
- // will reset it
- // thrA.setParam(null);
- // Next request
- if (!isAsync() || error) {
- inputBuffer.nextRequest();
- outputBuffer.nextRequest();
- }
- //hack keep alive behavior
- break;
- }
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
- if (error || endpoint.isPaused()) {
- recycle();
- return SocketState.CLOSED;
- } else if (isAsync()) {
- return SocketState.LONG;
- } else {
- if (!keepAlive) {
- recycle();
- return SocketState.CLOSED;
- } else {
- return SocketState.OPEN;
- }
- }
- }
这个方法比较长,我们看到在Processor中内置了inputBuffer、outputBuffer和request等对象,其中request和inputBuffer是个双向引用的关系:
- request = new Request();
- inputBuffer = new InternalInputBuffer(request, headerBufferSize);
- request.setInputBuffer(inputBuffer);
这样就可以在处理的时候达到同时处理两个对象的作用,然后在处理过程中可以看到inputBuffer设置了socket的输入流,然后分别调用其parseRequestLine、parseHeaders方法,具体方法内容初略一看就是处理这些输入的字节,有兴趣的可以和Http协议一起做详细的研究,我们这就先不展开了,总之处理完输入流的请求,然后调用prepareRequest方法,由于这部分代码的处理和协议内容关联比较大,如果没有非常熟悉http的数据包结构可能很难直接看源代码读懂,我的方法是断点调试,分别看清楚头尾的request参数有哪些不同,并着重看参数在哪部分代码运行之后起到变化。个人觉得这段代码给request传的值是最明显的:
- /**
- * Parse host.
- */
- protected void parseHost(MessageBytes valueMB) {
- if (valueMB == null || valueMB.isNull()) {
- // HTTP/1.0
- // Default is what the socket tells us. Overridden if a host is
- // found/parsed
- request.setServerPort(socket.getSocket().getLocalPort());
- InetAddress localAddress = socket.getSocket().getLocalAddress();
- // Setting the socket-related fields. The adapter doesn't know
- // about socket.
- request.serverName().setString(localAddress.getHostName());
- return;
- }
总而言之在给输入流做了处理以后填装进request中,并把request、response全部通过adapter.service处理
- /**
- * Service method.
- */
- @Override
- 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 = connector.createRequest();
- request.setCoyoteRequest(req);
- 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;
- boolean async = false;
- try {
- // Parse and set Catalina and configuration specific
- // request parameters
- req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
- boolean 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);
- 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.COMET_BEGIN, null);
- }
- } else {
- comet = true;
- res.action(ActionCode.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);
- }
- }
- }
- AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
- if (asyncConImpl != null) {
- async = true;
- } else if (!comet) {
- response.finishResponse();
- if (postParseSuccess) {
- // Log only if processing was invoked.
- // If postParseRequest() failed, it has already logged it.
- ((Context) request.getMappingData().context).logAccess(
- request, response,
- System.currentTimeMillis() - req.getStartTime(),
- false);
- }
- req.action(ActionCode.POST_REQUEST , null);
- }
- } catch (IOException e) {
- // Ignore
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- log.error(sm.getString("coyoteAdapter.service"), t);
- } finally {
- req.getRequestProcessor().setWorkerThreadName(null);
- // Recycle the wrapper request and response
- if (!comet && !async) {
- request.recycle();
- response.recycle();
- } else {
- // Clear converters so that the minimum amount of memory
- // is used by this processor
- request.clearEncoders();
- response.clearEncoders();
- }
- }
- }
所谓adapter,顾名思义就是适配不同的接口,这里我们也看到了,两套不同的request和response在我们眼前展现,第一套是刚才传入的org.apache.coyote包下的Request和Response、第二套就是我们之后要进入下一步操作的org.apache.catalina.connector下的Request和Response,在这段代码中有两行非常重要:
- boolean postParseSuccess = postParseRequest(req, request, res, response);
- connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
前者是映射内部参数,将上一步把socket输入流中传入的信息解析完成的request交给下一步调用的request处理,后一步则是调用接下来的过程。
我们先看前者,可能有人会要问为啥要那么麻烦开设两套request,其实一开始看代码的时候我也被两套请求类给搞懵了,由于没有对整个架构做过深层次分析,暂时还没有给这个问题准确的答案,但是我们可以提出假想来给后续的研究提供思路,据我而言,这种情况大致可能有两种:
第一种是Tomcat自己设计了一套请求类族,并完成了基本封装功能,但是由于这套类族不符合J2EE设计的类族规范,因此用适配器做适配,这也符合适配器模式本身的意图;
第二种是原本的第一次封装是在一个不分Http和AJP协议的Request中的,我们看Request的全名org.apache.coyote.Request,显然似乎没有做区分,而第二层封装,Request是实现了HttpServletRequest接口的,显然是专门为了Http协议设计的,因此过程中做适配,以达到第一层封装的可复用。
上述两种只是假想,先提出,后续研究来解答
我们继续看postParseRequest方法,由于这个方法比较长,我们截取较为重要的几句:
- String proxyName = connector.getProxyName();
- int proxyPort = connector.getProxyPort();
- if (proxyPort != 0) {
- req.setServerPort(proxyPort);
- }
- if (proxyName != null) {
- req.serverName().setString(proxyName);
- }
- //...........
- if (connector.getUseIPVHosts()) {
- serverName = req.localName();
- if (serverName.isNull()) {
- // well, they did ask for it
- res.action(ActionCode.REQ_LOCAL_NAME_ATTRIBUTE, null);
- }
- } else {
- serverName = req.serverName();
- }
- //.............
- connector.getMapper().map(serverName, decodedURI, version,
- request.getMappingData());
- request.setContext((Context) request.getMappingData().context);
- request.setWrapper((Wrapper) request.getMappingData().wrapper);
上述代码可以看到,从第一个request中取出了serverName、url等信息之后转入map方法,然后给第二个request的mappingData做初始化,最后给request设置基本值,这里已经把Context和Wrapper两个容器都设置在了request中,后续将会有大用处。至于map方法调用来调用去的我们具体不说了大家自己看,简单说下就是还记得上一篇讲到容器启动时候初始化完成以后,会有一个mappingListener.init方法给容器进行注册吗?这里就是从注册的容器中选出对应的四个容器并传入request.mappingData。
然后我们再回头这段代码:connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
前两个调用很好理解,仔细看过上一篇就明白获取到的Container就是engine,而每个容器其实都有一个pipeline,这个pipeline顾名思义是管道的意思,它的命名来源从我阅读的博客来看,是将父子容器的调用关联起来,请求先通过父容器再传输到子容器,其过程就如同管道运输分流一般,而管道中获取valve之后调用invoke方法,由于有四个容器,那么自然有四个valve,我们来看两个,分别hostValve和wrapperValve:
- /**
- * 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.
- *
- * @param request Request to be processed
- * @param response Response to be produced
- *
- * @exception IOException if an input/output error occurred
- * @exception ServletException if a servlet error occurred
- */
- @Override
- 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.
- if (Globals.IS_SECURITY_ENABLED) {
- PrivilegedAction<Void> pa = new PrivilegedSetTccl(
- context.getLoader().getClassLoader());
- AccessController.doPrivileged(pa);
- } else {
- Thread.currentThread().setContextClassLoader
- (context.getLoader().getClassLoader());
- }
- }
- if (request.isAsyncSupported()) {
- request.setAsyncSupported(context.getPipeline().isAsyncSupported());
- }
- // 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 (ACCESS_SESSION) {
- request.getSession(false);
- }
- // Error page processing
- response.setSuspended(false);
- Throwable t = (Throwable) request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION);
- if (t != null) {
- throwable(request, response, t);
- } else {
- status(request, response);
- }
- // Restore the context classloader
- if (Globals.IS_SECURITY_ENABLED) {
- PrivilegedAction<Void> pa = new PrivilegedSetTccl(
- StandardHostValve.class.getClassLoader());
- AccessController.doPrivileged(pa);
- } else {
- Thread.currentThread().setContextClassLoader
- (StandardHostValve.class.getClassLoader());
- }
- }
host中很长一段,但是其核心主要就是context.getPipeline().getFirst().invoke(request, response);,从request中获取context(上述map过程中传入的),并调用子容器valve的invoke,这里也可以明白父子管道的连接是如何实现的。
最终自然调用到了wrapperValve:
- /**
- * Invoke the servlet we are managing, respecting the rules regarding
- * servlet lifecycle and SingleThreadModel support.
- *
- * @param request Request to be processed
- * @param response Response to be produced
- *
- * @exception IOException if an input/output error occurred
- * @exception ServletException if a servlet error occurred
- */
- @Override
- 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();
- // Check for the application being marked unavailable
- if (!context.getAvailable()) {
- response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
- sm.getString("standardContext.isUnavailable"));
- unavailable = true;
- }
- // Check for the servlet being marked unavailable
- 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;
- }
- // Allocate a servlet instance to process this request
- 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);
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(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);
- }
- // Acknowledge the request
- try {
- response.sendAcknowledgement();
- } catch (IOException e) {
- container.getLogger().warn(sm.getString("standardWrapper.acknowledgeException",
- wrapper.getName()), e);
- throwable = e;
- exception(request, response, e);
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString("standardWrapper.acknowledgeException",
- wrapper.getName()), e);
- throwable = e;
- exception(request, response, e);
- servlet = null;
- }
- MessageBytes requestPathMB = request.getRequestPathMB();
- DispatcherType dispatcherType = DispatcherType.REQUEST;
- if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
- request.setAttribute
- (ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
- dispatcherType);
- request.setAttribute
- (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
- requestPathMB);
- // Create the filter chain for this request
- ApplicationFilterFactory factory =
- ApplicationFilterFactory.getInstance();
- ApplicationFilterChain filterChain =
- factory.createFilterChain(request, wrapper, servlet);
- // Reset comet flag value after creating the filter chain
- request.setComet(false);
- // Call the filter chain for this request
- // NOTE: This also calls the servlet's service() method
- try {
- if ((servlet != null) && (filterChain != null)) {
- // Swallow output if needed
- if (context.getSwallowOutput()) {
- try {
- SystemLogHandler.startCapture();
- if (request.isAsyncDispatching()) {
- //TODO SERVLET3 - async
- ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
- } else 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 (request.isAsyncDispatching()) {
- //TODO SERVLET3 - async
- ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
- } else if (comet) {
- request.setComet(true);
- filterChain.doFilterEvent(request.getEvent());
- } else {
- filterChain.doFilter
- (request.getRequest(), response.getResponse());
- }
- }
- }
- } catch (ClientAbortException e) {
- throwable = e;
- exception(request, response, e);
- } catch (IOException e) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- throwable = e;
- exception(request, response, e);
- } catch (UnavailableException e) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.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) {
- Throwable rootCause = StandardWrapper.getRootCause(e);
- if (!(rootCause instanceof ClientAbortException)) {
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceExceptionRoot",
- wrapper.getName(), context.getName(), e.getMessage()),
- rootCause);
- }
- throwable = e;
- exception(request, response, e);
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- container.getLogger().error(sm.getString(
- "standardWrapper.serviceException", wrapper.getName(),
- context.getName()), e);
- throwable = e;
- exception(request, response, e);
- }
- // Release the filter chain (if any) for this request
- 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();
- }
- }
- // Deallocate the allocated servlet instance
- try {
- if (servlet != null) {
- wrapper.deallocate(servlet);
- }
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(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) {
- ExceptionUtils.handleThrowable(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;
- }
我们看到这里用了servlet = wrapper.allocate();步骤来生成servlet,然后调用了servlet的service方法去处理request和Response,至于后续的service转入doPost和doGet是比较好理解的,我们主要来看servlet是如何被生成的:
- if (instance == null) {
- synchronized (this) {
- if (instance == null) {
- try {
- if (log.isDebugEnabled())
- log.debug("Allocating non-STM instance");
- 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) {
- ExceptionUtils.handleThrowable(e);
- throw new ServletException
- (sm.getString("standardWrapper.allocate"), e);
- }
- }
- }
请求的处理过程大体就是这样,刚刚着重讲的是request,其实在这个流程中response应该也明白了,还记得一开始的outputBuffer被填装进入了第一个Response中吗,只要在适配过程中给第二个response传入这个输出流,那么response的print方法就非常容易实现了。
在后续的篇章中我们将继续研究Tomcat的很多细节,如servletContext啊、session啊,以及我们将会和J2EE的规范一起来研究整体的架构设计。
- 浅读Tomcat源码(三)---请求处理
- 浅读Tomcat源码(三)---请求处理
- Tomcat源码解读系列(三)——Tomcat对HTTP请求处理的整体流程
- Tomcat源码解读系列(三)——Tomcat对HTTP请求处理的整体流程
- Tomcat请求处理(三) -- coyote请求处理
- Tomcat请求处理过程(Tomcat源码解析五)
- Tomcat请求处理过程(Tomcat源码解析五)
- Tomcat处理HTTP请求源码分析(上)
- Tomcat处理HTTP请求源码分析(上)
- Tomcat处理HTTP请求源码分析(上)
- Tomcat处理HTTP请求源码分析(下)
- Tomcat处理HTTP请求源码分析(下)
- Tomcat处理HTTP请求源码分析(上)
- Tomcat处理HTTP请求源码分析(下)
- Tomcat处理HTTP请求源码分析(上)
- Tomcat处理HTTP请求源码分析(上)
- Tomcat处理HTTP请求源码分析(下)
- Tomcat处理HTTP请求源码分析(上)
- LeetCode-153:Find Minimum in Rotated Sorted Array (可能旋转的排序数组中的最小值) -- medium
- building-gcc-4-8-from-source-on-ubunu-12-04/
- 面向对象程序设计原则
- ubuntu16.04 运行dso_ros
- IDEA安装插件
- 浅读Tomcat源码(三)---请求处理
- webservice示例获取城市天气
- windows环境Python配置:同时安装Python2.7和Python3.6开发环境
- java8 利用 stream,lambda 将一个数组转化为字符串String
- ubuntu 16.04的快捷键
- bootstrap模态框弹出后如何获得焦点
- 浅读Tomcat源码(四)---session、servletContext、listener浅析
- c语言—三子棋
- Kali Linux2017更新源