Tomcat架構(gòu)入門-請求轉(zhuǎn)換

作為Java開發(fā)人員來說Tomcat應(yīng)該在熟悉不過了,自己最近空閑時間比較多,前天突然很好奇到底用戶的請求是怎么被轉(zhuǎn)化成HttpServletRequest,所以自己想著看看源碼,但是因為考慮到一直以來自己對 Tomcat的架構(gòu)并不熟悉,所以我決定在看源碼前好好的熟悉下Tomcat的架構(gòu),看看Tomcat都有哪些組件以及它們的作用分別是什么,這個過程我主要是參考官方文檔,另外就是Tomcat的源碼。

一、Tomcat的組成

關(guān)于Tomcat的架構(gòu)其實(shí)可以通過Tomcat下的server.xml做一個基本了解。

<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <GlobalNamingResources>

    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <Service name="Catalina">

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    <Engine name="Catalina" defaultHost="localhost">

      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>

然后通過網(wǎng)上的一張架構(gòu)圖來了解各個層級之間的關(guān)系


tomcat整體架構(gòu).jpg

二、核心組件

結(jié)合這server.xml和上面這張圖,來簡單的了解一下各個組件。
Server是Tomcat的頂層組件。在server.xmlServer元素表示整個Catalina Servlet容器。因此,它必須是server.xml配置文件中的單個最外層元素,簡單一點(diǎn)可以將Server看做是Tomcat服務(wù)器本身,Server管理著整個Tomcat生命周期。一個Server內(nèi)可以包了多個listener,可以包含一個或多個Service。
Service是Tomcat的另一個頂層組件,Service元素由一個或多個Connector組件的組合,這些Connector共享一個Engine組件以處理傳入的請求。Service的功能就是對外提供服務(wù)。
接下來是Tomcat的幾個比較重要的組件:
Connector顧名思義就是連接器,它連接的是用戶請求和容器,即用戶的請求到達(dá)Connector后,由它將用戶請求進(jìn)行包裝,然后傳遞給具體的容器Engine進(jìn)行處理,之后再通過Connector將結(jié)果進(jìn)行包裝轉(zhuǎn)換傳遞給用戶。Connector功能就是處理用戶的請求和響應(yīng)。Service中的Engine對外是不可見的,所有與Engine的交互必須先經(jīng)過Connector處理。目前Tomcat有三種Connector,它們主要區(qū)別在于支持的協(xié)議不同。最常見的就是支持http/1.1,另外還有支持http/2以及ajp(Apache Jserv Protocol,一種二進(jìn)制協(xié)議)。關(guān)于這個三種連接器上相關(guān)的屬性建議看下官方文檔,因為內(nèi)容還是比較多的。在對Tomcat進(jìn)行優(yōu)化事很多時候都會需要修改Connector上的相關(guān)參數(shù),比如最大連接數(shù)、最大線程數(shù)量等等。所以這部分內(nèi)容是非常重要的,也是核心。
Engine代表的處理請求的入口。它接受并處理來自Connector的所有請求,并將完成的響應(yīng)返回給連接器,最終傳輸回客戶端。
HostEngine的子容器,一個Engine內(nèi)可以有一個或多個Host,它表示的一個虛擬主機(jī),
Context表示在特定虛擬主機(jī)中運(yùn)行的Web應(yīng)用程序,每一個Web應(yīng)用程序都基于WAR文件或包含相應(yīng)解壓縮內(nèi)容的相應(yīng)目錄。每一個虛擬主機(jī)中可以包含多個Context。
Service中還有一個很重要的組件就是Executor,Executor表示是Tomcat中的組件之間共享的線程池。 Tomcat已經(jīng)默認(rèn)為每個連接器都創(chuàng)建了一個線程池。自定義線程池可以供Connector共享,也可以和其他組件共享該線程池。
除了上面的核心組件之外,還有其他一些嵌套組件,比如server.xml中定義的Listener、RealmGlobalNamingResources等。這里就不細(xì)述了。
下圖是一個用戶請求和響應(yīng)的流程示意圖,實(shí)際情況當(dāng)然要復(fù)雜許多。

請求流程.jpg

三、請求轉(zhuǎn)換

下面主要看看Connector是如何將用戶請求一步一步進(jìn)行轉(zhuǎn)換的,我們先看下對應(yīng)類的繼承體系

Connector.jpg

Connector封裝了兩個主要的成員變量,一個是ProtocolHandler,一個是Adapter,前者根據(jù)不同的協(xié)議有不同的實(shí)現(xiàn)類型,我們以Http11NioProtocol為例,其類的繼承體系如下
Http11NioProtocol.jpg

ProtocolHandler主要是處理網(wǎng)絡(luò)連接,將字節(jié)流封裝成 Request對象,再將Request 適配成 Servlet 處理ServletRequest 對象這幾個動作,用組件封裝起來了,ProtocolHandler包括了三個組件:Endpoint、ProcessorAdapter。
Endpoint的創(chuàng)建則在其默認(rèn)構(gòu)造函數(shù)實(shí)現(xiàn),以Http11NioProtocol為例,其代碼如下:

   public Http11NioProtocol() {
        super(new NioEndpoint());
    }

Endpoint主要用來處理底層的Socket網(wǎng)絡(luò)連接,在Endpoint具體實(shí)現(xiàn)類中里面有個SocketProcessor的內(nèi)部類,它負(fù)責(zé)將Endpoint接收到的Socket請求轉(zhuǎn)化成org.apache.coyote.Request請求。
代碼如下:

    protected class SocketProcessor extends SocketProcessorBase<NioChannel> {

        public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
            super(socketWrapper, event);
        }

        @Override
        protected void doRun() {
            NioChannel socket = socketWrapper.getSocket();
            SelectionKey key = socket.getIOChannel().keyFor(socket.getSocketWrapper().getPoller().getSelector());
            Poller poller = NioEndpoint.this.poller;
            if (poller == null) {
                socketWrapper.close();
                return;
            }
            try {
                int handshake = -1;
                try {
                    if (key != null) {
                        if (socket.isHandshakeComplete()) {

                            handshake = 0;
                        } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                                event == SocketEvent.ERROR) {
                            handshake = -1;
                        } else {
                            handshake = socket.handshake(key.isReadable(), key.isWritable());
                            event = SocketEvent.OPEN_READ;
                        }
                    }
                } catch (IOException x) {
                    handshake = -1;
                    if (log.isDebugEnabled()) log.debug("Error during SSL handshake",x);
                } catch (CancelledKeyException ckx) {
                    handshake = -1;
                }
                if (handshake == 0) {
                    SocketState state = SocketState.OPEN;
                    // Process the request from this socket
                    if (event == null) {
                        state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                    } else {
                        state = getHandler().process(socketWrapper, event);
                    }
                    if (state == SocketState.CLOSED) {
                        poller.cancelledKey(key, socketWrapper);
                    }
                } else if (handshake == -1 ) {
                    poller.cancelledKey(key, socketWrapper);
                } else if (handshake == SelectionKey.OP_READ){
                    socketWrapper.registerReadInterest();
                } else if (handshake == SelectionKey.OP_WRITE){
                    socketWrapper.registerWriteInterest();
                }
            } catch (CancelledKeyException cx) {
                poller.cancelledKey(key, socketWrapper);
            } catch (VirtualMachineError vme) {
                ExceptionUtils.handleThrowable(vme);
            } catch (Throwable t) {
                log.error(sm.getString("endpoint.processing.fail"), t);
                poller.cancelledKey(key, socketWrapper);
            } finally {
                socketWrapper = null;
                event = null;
                //return to cache
                if (running && !paused && processorCache != null) {
                    processorCache.push(this);
                }
            }
        }
    }

代碼中getHandler().process()方法會創(chuàng)建一個Processor對象,因為代碼較多,我只粘貼一部分:

public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
          if (wrapper == null) {
              return SocketState.CLOSED;
          }
          S socket = wrapper.getSocket();
          Processor processor = connections.get(socket);
            ....
          if (processor == null) {
                  String negotiatedProtocol = wrapper.getNegotiatedProtocol();
                  if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) {
                      UpgradeProtocol upgradeProtocol = getProtocol().getNegotiatedProtocol(negotiatedProtocol);
                      if (upgradeProtocol != null) {
                          processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
                      } else if (negotiatedProtocol.equals("http/1.1")) {
                      } else {
                          if (getLog().isDebugEnabled()) {
                              getLog().debug(sm.getString("abstractConnectionHandler.negotiatedProcessor.fail",negotiatedProtocol));
                          }
                          return SocketState.CLOSED;
                      }
                  }
              }
              if (processor == null) {
                  processor = recycledProcessors.pop();
                  if (getLog().isDebugEnabled()) {
                        getLog().debug(sm.getString("abstractConnectionHandler.processorPop",processor));
                  }
              }
              if (processor == null) {
                  processor = getProtocol().createProcessor();
                  register(processor);
              }
      // 省略代碼
      ......
}

上面的代碼在AbstractProtocol的內(nèi)部類ConnectionHandler中,其中socket變量其實(shí)是一個NioChannel實(shí)例。這個方法代碼中會根據(jù)不同情形創(chuàng)建相關(guān)的Processor。比如我這里會獲取相應(yīng)的AbstractHttp11Protocol然后調(diào)用其createProcessor()方法創(chuàng)建Http11Processor對象。

    @Override
    protected Processor createProcessor() {
        Http11Processor processor = new Http11Processor(this, adapter);
        return processor;
    }

adapter其實(shí)在Connector初始化的時候就創(chuàng)建完成了,并將其添加到protocolHandler中,下面是ConnectorinitInternal()方法:

    @Override
    protected void initInternal() throws LifecycleException {
        super.initInternal();
        .......
        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);
        if (service != null) {
            protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
        }
      ......

另外需要關(guān)注的一點(diǎn)是創(chuàng)建Http11Processor的時候,會調(diào)用其父類的構(gòu)造函數(shù):

    public Http11Processor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
        super(adapter);
        this.protocol = protocol;

        httpParser = new HttpParser(protocol.getRelaxedPathChars(),
                protocol.getRelaxedQueryChars());

        inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
                protocol.getRejectIllegalHeaderName(), httpParser);
        request.setInputBuffer(inputBuffer);

        outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
        response.setOutputBuffer(outputBuffer);
        // 省略代碼
        .....
    }

而調(diào)用父類構(gòu)造函數(shù)的時候會創(chuàng)建兩個對象,RequestResponse,對應(yīng)的具體類是org.apache.coyote.Requestorg.apache.coyote.Response。這兩個對象下面還會使用到,不要混淆了。
回歸到正題,Processor創(chuàng)建完成后會調(diào)用它的process方法(AbstractProcessorLight.process)其方法內(nèi)部會調(diào)用具體的service方法,這里調(diào)用的是Http11Processor.service方法,這個方法內(nèi)內(nèi)容很多,我也并沒有仔細(xì)的看,應(yīng)該是對org.apache.coyote.Requestorg.apache.coyote.Response進(jìn)行了參數(shù)設(shè)置,核心的地方在于調(diào)用adapterservice方法,代碼如下:

            if (getErrorState().isIoAllowed()) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    // 適配器轉(zhuǎn)換,request和response
                    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);
                } catch (HeadersTooLargeException e) {
                    log.error(sm.getString("http11processor.request.process"), e);
                    if (response.isCommitted()) {
                        setErrorState(ErrorState.CLOSE_NOW, e);
                    } else {
                        response.reset();
                        response.setStatus(500);
                        setErrorState(ErrorState.CLOSE_CLEAN, e);
                        response.setHeader("Connection", "close"); // TODO: Remove
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("http11processor.request.process"), t);
                    response.setStatus(500);
                    setErrorState(ErrorState.CLOSE_CLEAN, t);
                    getAdapter().log(request, response, 0);
                }
            }

這里會調(diào)用CoyoteAdapterservice方法,代碼如下:

    @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) {
            request = connector.createRequest();
            request.setCoyoteRequest(req);
            response = connector.createResponse();
            response.setCoyoteResponse(res);

            request.setResponse(response);
            response.setRequest(request);

            req.setNote(ADAPTER_NOTES, request);
            res.setNote(ADAPTER_NOTES, response);
            req.getParameters().setQueryStringCharset(connector.getURICharset());
        }

        if (connector.getXpoweredBy()) {
            response.addHeader("X-Powered-By", POWERED_BY);
        }
        boolean async = false;
        boolean postParseSuccess = false;
        req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
        try {
            postParseSuccess = postParseRequest(req, request, res, response);
            if (postParseSuccess) {
                request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
                connector.getService().getContainer().getPipeline().getFirst().invoke(
                        request, response);
            }
            if (request.isAsync()) {
                async = true;
                ReadListener readListener = req.getReadListener();
                if (readListener != null && request.isFinished()) {
                    ClassLoader oldCL = null;
                    try {
                        oldCL = request.getContext().bind(false, null);
                        if (req.sendAllDataReadEvent()) {
                            req.getReadListener().onAllDataRead();
                        }
                    } finally {
                        request.getContext().unbind(false, oldCL);
                    }
                }
                Throwable throwable =(Throwable)request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);

                if (!request.isAsyncCompleting() && throwable != null) {
                    request.getAsyncContextInternal().setErrorState(throwable, true);
                }
            } else {
                request.finishRequest();
                response.finishResponse();
            }

        } catch (IOException e) {
            // Ignore
        } finally {
            AtomicBoolean error = new AtomicBoolean(false);
            res.action(ActionCode.IS_ERROR, error);
            if (request.isAsyncCompleting() && error.get()) {
                res.action(ActionCode.ASYNC_POST_PROCESS,  null);
                async = false;
            }

            // Access log
            if (!async && postParseSuccess) {
                Context context = request.getContext();
                Host host = request.getHost();
                long time = System.currentTimeMillis() - req.getStartTime();
                if (context != null) {
                    context.logAccess(request, response, time, false);
                } else if (response.isError()) {
                    if (host != null) {
                        host.logAccess(request, response, time, false);
                    } else {
                        connector.getService().getContainer().logAccess(request, response, time, false);
                    }
                }
            }

            req.getRequestProcessor().setWorkerThreadName(null);
            if (!async) {
                updateWrapperErrorCount(request, response);
                request.recycle();
                response.recycle();
            }
        }
    }

這個方法的入?yún)?code>org.apache.coyote.Request和org.apache.coyote.Response兩個變量,方法內(nèi)會根據(jù)入?yún)?chuàng)建出org.apache.catalina.connector.Requestorg.apache.catalina.connector.Response,而這兩個對象分別繼承了HttpServletRequestHttpServletResponse,也就是說實(shí)際上用戶請求在這里完成了轉(zhuǎn)換,變成了我們非常熟悉的HttpServletRequestHttpServletResponse。
然后執(zhí)行下面這段代碼:

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

即通過Connector一步步將requestresponse丟給容器進(jìn)行處理,而且從方法名稱也可以看的出來,實(shí)際上是由Pipeline進(jìn)行處理的,這個就暫不深究了。也就是說到這里我們的HttpServletRequestHttpServletResponse會被容器進(jìn)行處理了,在Connector的流程執(zhí)行完成,之后就是有容器進(jìn)行處理過程了,這里就不再繼續(xù)往下看了。
整個Connector執(zhí)行流程如下圖:

請求轉(zhuǎn)換流程圖.png

通過以上代碼和流程圖基本上就搞清楚了,用戶請求是如何變成我們熟悉的HttpServletRequestHttpServletResponse的,當(dāng)然實(shí)際過程要復(fù)雜很多,我只是簡單的通過跟蹤代碼了解了大概的過程,具體代碼內(nèi)容并沒有詳細(xì)的去看,后期如果有需要的話自己會挑選一部分進(jìn)行閱讀,比如今天的Connector這部分。


本次的學(xué)習(xí)收獲主要有兩點(diǎn)一是Tomcat的架構(gòu),即其組成及各組件的作用,當(dāng)然這部分沒有去深入學(xué)習(xí),二是請求轉(zhuǎn)換,即用戶請求過來之后是如何轉(zhuǎn)換成HttpServletRequestHttpServletResponse的,這部分主要理清了大概的執(zhí)行流程,并簡單的跟蹤了代碼,如果后期有需要會在來具體的分析這部分代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容