日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

Tomcat的系统架构(以Tomcat5为基础)

發布時間:2025/5/22 windows 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Tomcat的系统架构(以Tomcat5为基础) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Tomcat總體設計

?????? Tomcat的總體結構圖:

?

????? 由上圖可知,Tomcat有兩個核心組件:Connector和Container。Connector組件是可以被替換的,這樣可以給服務器設計者提供更多選擇,它非常重要。它不僅跟服務器本身設計相關,而且和不同的應用場景也很相關,所以一個Container可以選擇對應多個Connector。多個Connector與一個Container就形成了一個Service,這樣就可以對外提供服務了。但是Service還要有一個生存環境,必須能給其生命、掌握其生死大權,這時就非Server莫屬了。所以整個Tomcat的生命周期由Server控制。

????? 1.以Service為媒介

?????? Service只是在Connector和Container外面多包了一層,把它們組裝在了一起,向外面提供服務。一個Service可以設置多個Connector,但是只能有一個Container容器。

這個Service接口的方法列表如下圖:

????? 從Service接口中定義的方法知道,它主要是為了關聯Connector和Container,同時會初始化它下面的其它組件(但不控制它們的生命周期)。在Tomcat中Service接口的標準實現類是StandardService,它不僅實現了Service接口,同時還實現了Lifecycle接口,這樣它就可以控制下面組件的生命周期了。StandardService類結構圖如下:

?

????? 由上圖可知,除了Service接口的方法的實現以及控制組件生命周期的Lifecycle接口的實現,還有幾個方法用于實現事件監聽方法。不僅是這個Service組件,在Tomcat中其它組件也同樣有這幾個方法。

????? 下面是StandardService中的幾個主要方法:

?????? ? ? ?

?????? 2.以Server為“居”

?????? 前面說一對情侶因為 Service 而成為一對夫妻,有了能夠組成一個家庭的基本條件,但是它們還要有個實體的家,這是它們在社會上生存之本,有了家它們就可以安心的為人民服務了,一起為社會創造財富。

????? Server 要完成的任務很簡單,就是要能夠提供一個接口讓其它程序能夠訪問到這個 Service 集合、同時要維護它所包含的所有 Service 的生命周期,包括如何初始化、如何結束服務、如何找到別人要訪問的 Service。還有其它的一些次要的任務,如您住在這個地方要向當地政府去登記啊、可能還有要配合當地公安機關日常的安全檢查什么的。

????? Server 的類結構圖如下:

????? 它的標準實現類 StandardServer 實現了上面這些方法,同時也實現了 Lifecycle、MbeanRegistration 兩個接口的所有方法,下面主要看一下 StandardServer 重要的一個方法 addService 的實現:

清單 3. StandardServer.addService
public void addService(Service service) {service.setServer(this);synchronized (services) {Service results[] = new Service[services.length + 1];System.arraycopy(services, 0, results, 0, services.length);results[services.length] = service;services = results;if (initialized) {try {service.initialize();} catch (LifecycleException e) {e.printStackTrace(System.err);}}if (started && (service instanceof Lifecycle)) {try {((Lifecycle) service).start();} catch (LifecycleException e) {;}}support.firePropertyChange("service", null, service);} }

????? 從上面第一句就知道了 Service 和 Server 是相互關聯的,Server 也是和 Service 管理 Connector 一樣管理它,也是將 Service 放在一個數組中,后面部分的代碼也是管理這個新加進來的 Service 的生命周期。Tomcat6 中也是沒有什么變化的。

?

????? 3.組件的生命線“Lifecycle”?????

?????? 前面一直在說 Service 和 Server 管理它下面組件的生命周期,那它們是如何管理的呢?

?????? Tomcat 中組件的生命周期是通過 Lifecycle 接口來控制的,組件只要繼承這個接口并實現其中的方法就可以統一被擁有它的組件控制了,這樣一層一層的直到一個最高級的組件就可以控制 Tomcat 中所有組件的生命周期,這個最高的組件就是 Server,而控制 Server 的是 Startup,也就是您啟動和關閉 Tomcat。

?????? 下面是 Lifecycle 接口的類結構圖:

????? 除了控制生命周期的 Start 和 Stop 方法外還有一個監聽機制,在生命周期開始和結束的時候做一些額外的操作。這個機制在其它的框架中也被使用,如在 Spring 中。關于這個設計模式會在后面介紹。

????? Lifecycle 接口的方法的實現都在其它組件中,就像前面中說的,組件的生命周期由包含它的父組件控制,所以它的 Start 方法自然就是調用它下面的組件的 Start 方法,Stop 方法也是一樣。如在 Server 中 Start 方法就會調用 Service 組件的 Start 方法,Server 的 Start 方法代碼如下:

?????? 清單 4. StandardServer.Start
public void start() throws LifecycleException {if (started) {log.debug(sm.getString("standardServer.start.started"));return;}lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);lifecycle.fireLifecycleEvent(START_EVENT, null);started = true;synchronized (services) {for (int i = 0; i < services.length; i++) {if (services[i] instanceof Lifecycle)((Lifecycle) services[i]).start();}}lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }

???????監聽的代碼會包圍 Service 組件的啟動過程,就是簡單的循環啟動所有 Service 組件的 Start 方法,但是所有 Service 必須要實現 Lifecycle 接口,這樣做會更加靈活。

?????? Server 的 Stop 方法代碼如下:

清單 5. StandardServer.Stop
public void stop() throws LifecycleException {if (!started)return;lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);lifecycle.fireLifecycleEvent(STOP_EVENT, null);started = false;for (int i = 0; i < services.length; i++) {if (services[i] instanceof Lifecycle)((Lifecycle) services[i]).stop();}lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null); }

?????? 它所要做的事情也和 Start 方法差不多。

?

?

Connector 組件

????? Connector 組件是 Tomcat 中兩個核心組件之一,它的主要任務是負責接收瀏覽器的發過來的 tcp 連接請求,創建一個 Request 和 Response 對象分別用于和請求端交換數據,然后會產生一個線程來處理這個請求并把產生的 Request 和 Response 對象傳給處理這個請求的線程,處理這個請求的線程就是 Container 組件要做的事了。

????? 由于這個過程比較復雜,大體的流程可以用下面的順序圖來解釋:

??????? 圖 6. Connector 處理一次請求順序圖

?

??????Tomcat5 中默認的 Connector 是 Coyote,這個 Connector 是可以選擇替換的。Connector 最重要的功能就是接收連接請求然后分配線程讓 Container 來處理這個請求,所以這必然是多線程的,多線程的處理是 Connector 設計的核心。Tomcat5 將這個過程更加細化,它將 Connector 劃分成 Connector、Processor、Protocol, 另外 Coyote 也定義自己的 Request 和 Response 對象。

????? 下面主要看一下 Tomcat 中如何處理多線程的連接請求,先看一下 Connector 的主要類圖:

??????? 圖 7. Connector 的主要類圖

????? 看一下 HttpConnector 的 Start 方法:

??????? 清單 6. HttpConnector.Start
public void start() throws LifecycleException {if (started)throw new LifecycleException(sm.getString("httpConnector.alreadyStarted"));threadName = "HttpConnector[" + port + "]";lifecycle.fireLifecycleEvent(START_EVENT, null);started = true;threadStart();while (curProcessors < minProcessors) {if ((maxProcessors > 0) && (curProcessors >= maxProcessors))break;HttpProcessor processor = newProcessor();recycle(processor);} }

?????? threadStart() 執行就會進入等待請求的狀態,直到一個新的請求到來才會激活它繼續執行,這個激活是在 HttpProcessor 的 assign 方法中,這個方法是代碼如下?:

?????? 清單 7. HttpProcessor.assign
synchronized void assign(Socket socket) {while (available) {try {wait();} catch (InterruptedException e) {}}this.socket = socket;available = true;notifyAll();if ((debug >= 1) && (socket != null))log(" An incoming request is being assigned"); }

????? 創建 HttpProcessor 對象是會把 available 設為 false,所以當請求到來時不會進入 while 循環,將請求的 socket 賦給當期處理的 socket,并將 available 設為 true,當 available 設為 true 是 HttpProcessor 的 run 方法將被激活,接下去將會處理這次請求。

????? Run 方法代碼如下:

?????? 清單 8. HttpProcessor.Run
public void run() { while (!stopped) { Socket socket = await(); if (socket == null) continue; try { process(socket); } catch (Throwable t) { log("process.invoke", t); } connector.recycle(this); } synchronized (threadSync) { threadSync.notifyAll(); } }

????? 解析 socket 的過程在 process 方法中,process 方法的代碼片段如下:

?????? 清單 9. HttpProcessor.process
private void process(Socket socket) {boolean ok = true;boolean finishResponse = true;SocketInputStream input = null;OutputStream output = null;try {input = new SocketInputStream(socket.getInputStream(),connector.getBufferSize());} catch (Exception e) {log("process.create", e);ok = false;}keepAlive = true;while (!stopped && ok && keepAlive) {finishResponse = true;try {request.setStream(input);request.setResponse(response);output = socket.getOutputStream();response.setStream(output);response.setRequest(request);((HttpServletResponse) response.getResponse()).setHeader("Server", SERVER_INFO);} catch (Exception e) {log("process.create", e);ok = false;}try {if (ok) {parseConnection(socket);parseRequest(input, output);if (!request.getRequest().getProtocol().startsWith("HTTP/0"))parseHeaders(input);if (http11) {ackRequest(output);if (connector.isChunkingAllowed())response.setAllowChunking(true);}}。。。。。。try {((HttpServletResponse) response).setHeader("Date", FastHttpDateFormat.getCurrentDate());if (ok) {connector.getContainer().invoke(request, response);}。。。。。。}try {shutdownInput(input);socket.close();} catch (IOException e) {;} catch (Throwable e) {log("process.invoke", e);}socket = null; }

????? 當 Connector 將 socket 連接封裝成 request 和 response 對象后接下來的事情就交給 Container 來處理了。

??? Servlet 容器“Container”

????? Container 是容器的父接口,所有子容器都必須實現這個接口,Container 容器的設計用的是典型的責任鏈的設計模式,它有四個子容器組件構成,分別是:Engine、Host、Context、Wrapper,這四個組件不是平行的,而是父子關系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一個 Servlet class 對應一個 Wrapper,如果有多個 Servlet 就可以定義多個 Wrapper,如果有多個 Wrapper 就要定義一個更高的 Container 了,如 Context,Context 通常就是對應下面這個配置:

清單 10. Server.xml
<Context path="/library"docBase="D:\projects\library\deploy\target\library.war" reloadable="true" />

????? 容器的總體設計

????? Context 還可以定義在父容器 Host 中,Host 不是必須的,但是要運行 war 程序,就必須要 Host,因為 war 中必有 web.xml 文件,這個文件的解析就需要 Host 了,如果要有多個 Host 就要定義一個 top 容器 Engine 了。而 Engine 沒有父容器了,一個 Engine 代表一個完整的 Servlet 引擎。

那么這些容器是如何協同工作的呢?先看一下它們之間的關系圖:

圖 8. 四個容器的關系圖

(查看清晰大圖)

????? 當 Connector 接受到一個連接請求時,將請求交給 Container,Container 是如何處理這個請求的?這四個組件是怎么分工的,怎么把請求傳給特定的子容器的呢?又是如何將最終的請求交給 Servlet 處理。下面是這個過程的時序圖:

??????? 圖 9. Engine 和 Host 處理請求的時序圖

(查看清晰大圖)

????? 這里看到了 Valve 是不是很熟悉,沒錯 Valve 的設計在其他框架中也有用的,同樣 Pipeline 的原理也基本是相似的,它是一個管道,Engine 和 Host 都會執行這個 Pipeline,您可以在這個管道上增加任意的 Valve,Tomcat 會挨個執行這些 Valve,而且四個組件都會有自己的一套 Valve 集合。您怎么才能定義自己的 Valve 呢?在 server.xml 文件中可以添加,如給 Engine 和 Host 增加一個 Valve 如下:

????? 清單 11. Server.xml
<Engine defaultHost="localhost" name="Catalina"><Valve className="org.apache.catalina.valves.RequestDumperValve"/>………<Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false"><Valve className="org.apache.catalina.valves.FastCommonAccessLogValve"directory="logs" prefix="localhost_access_log." suffix=".txt"pattern="common" resolveHosts="false"/> …………</Host> </Engine>

????? StandardEngineValve 和 StandardHostValve 是 Engine 和 Host 的默認的 Valve,它們是最后一個 Valve 負責將請求傳給它們的子容器,以繼續往下執行。

前面是 Engine 和 Host 容器的請求過程,下面看 Context 和 Wrapper 容器時如何處理請求的。下面是處理請求的時序圖:

?????? 圖 10. Context 和 wrapper 的處理請求時序圖

(查看清晰大圖)

從 Tomcat5 開始,子容器的路由放在了 request 中,request 中保存了當前請求正在處理的 Host、Context 和 wrapper。

Engine 容器

Engine 容器比較簡單,它只定義了一些基本的關聯關系,接口類圖如下:

圖 11. Engine 接口的類結構

它的標準實現類是 StandardEngine,這個類注意一點就是 Engine 沒有父容器了,如果調用 setParent 方法時將會報錯。添加子容器也只能是 Host 類型的,代碼如下:

清單 12. StandardEngine. addChild
public void addChild(Container child) {if (!(child instanceof Host))throw new IllegalArgumentException(sm.getString("standardEngine.notHost"));super.addChild(child); }public void setParent(Container container) {throw new IllegalArgumentException(sm.getString("standardEngine.notParent")); }

它的初始化方法也就是初始化和它相關聯的組件,以及一些事件的監聽。

Host 容器

Host 是 Engine 的子容器,一個 Host 在 Engine 中代表一個虛擬主機,這個虛擬主機的作用就是運行多個應用,它負責安裝和展開這些應用,并且標識這個應用以便能夠區分它們。它的子容器通常是 Context,它除了關聯子容器外,還有就是保存一個主機應該有的信息。

下面是和 Host 相關的類關聯圖:

圖 12. Host 相關的類圖

(查看清晰大圖)

從上圖中除了所有容器都繼承的 ContainerBase 外,StandardHost 還實現了 Deployer 接口,上圖清楚的列出了這個接口的主要方法,這些方法都是安裝、展開、啟動和結束每個 web application。

Deployer 接口的實現是 StandardHostDeployer,這個類實現了的最重要的幾個方法,Host 可以調用這些方法完成應用的部署等。

Context 容器

Context 代表 Servlet 的 Context,它具備了 Servlet 運行的基本環境,理論上只要有 Context 就能運行 Servlet 了。簡單的 Tomcat 可以沒有 Engine 和 Host。

Context 最重要的功能就是管理它里面的 Servlet 實例,Servlet 實例在 Context 中是以 Wrapper 出現的,還有一點就是 Context 如何才能找到正確的 Servlet 來執行它呢? Tomcat5 以前是通過一個 Mapper 類來管理的,Tomcat5 以后這個功能被移到了 request 中,在前面的時序圖中就可以發現獲取子容器都是通過 request 來分配的。

Context 準備 Servlet 的運行環境是在 Start 方法開始的,這個方法的代碼片段如下:

清單 13. StandardContext.start
public synchronized void start() throws LifecycleException {………if( !initialized ) { try {init();} catch( Exception ex ) {throw new LifecycleException("Error initializaing ", ex);}}………lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);setAvailable(false);setConfigured(false);boolean ok = true;File configBase = getConfigBase();if (configBase != null) {if (getConfigFile() == null) {File file = new File(configBase, getDefaultConfigFile());setConfigFile(file.getPath());try {File appBaseFile = new File(getAppBase());if (!appBaseFile.isAbsolute()) {appBaseFile = new File(engineBase(), getAppBase());}String appBase = appBaseFile.getCanonicalPath();String basePath = (new File(getBasePath())).getCanonicalPath();if (!basePath.startsWith(appBase)) {Server server = ServerFactory.getServer();((StandardServer) server).storeContext(this);}} catch (Exception e) {log.warn("Error storing config file", e);}} else {try {String canConfigFile = (new File(getConfigFile())).getCanonicalPath();if (!canConfigFile.startsWith (configBase.getCanonicalPath())) {File file = new File(configBase, getDefaultConfigFile());if (copy(new File(canConfigFile), file)) {setConfigFile(file.getPath());}}} catch (Exception e) {log.warn("Error setting config file", e);}}}………Container children[] = findChildren();for (int i = 0; i < children.length; i++) {if (children[i] instanceof Lifecycle)((Lifecycle) children[i]).start();}if (pipeline instanceof Lifecycle)((Lifecycle) pipeline).start();………}

它主要是設置各種資源屬性和管理組件,還有非常重要的就是啟動子容器和 Pipeline。

我們知道 Context 的配置文件中有個 reloadable 屬性,如下面配置:

清單 14. Server.xml
<Context path="/library" docBase="D:\projects\library\deploy\target\library.war" reloadable="true" />

當這個 reloadable 設為 true 時,war 被修改后 Tomcat 會自動的重新加載這個應用。如何做到這點的呢 ? 這個功能是在 StandardContext 的 backgroundProcess 方法中實現的,這個方法的代碼如下:

清單 15. StandardContext. backgroundProcess
public void backgroundProcess() {if (!started) return;count = (count + 1) % managerChecksFrequency;if ((getManager() != null) && (count == 0)) {try {getManager().backgroundProcess();} catch ( Exception x ) {log.warn("Unable to perform background process on manager",x);}}if (getLoader() != null) {if (reloadable && (getLoader().modified())) {try {Thread.currentThread().setContextClassLoader(StandardContext.class.getClassLoader());reload();} finally {if (getLoader() != null) {Thread.currentThread().setContextClassLoader(getLoader().getClassLoader());}}}if (getLoader() instanceof WebappLoader) {((WebappLoader) getLoader()).closeJARs(false);}} }

它會調用 reload 方法,而 reload 方法會先調用 stop 方法然后再調用 Start 方法,完成 Context 的一次重新加載。可以看出執行 reload 方法的條件是 reloadable 為 true 和應用被修改,那么這個 backgroundProcess 方法是怎么被調用的呢?

這個方法是在 ContainerBase 類中定義的內部類 ContainerBackgroundProcessor 被周期調用的,這個類是運行在一個后臺線程中,它會周期的執行 run 方法,它的 run 方法會周期調用所有容器的 backgroundProcess 方法,因為所有容器都會繼承 ContainerBase 類,所以所有容器都能夠在 backgroundProcess 方法中定義周期執行的事件。

Wrapper 容器

Wrapper 代表一個 Servlet,它負責管理一個 Servlet,包括Servlet 的裝載、初始化、執行以及資源回收。Wrapper 是最底層的容器,它沒有子容器了,所以調用它的 addChild 將會報錯。

Wrapper 的實現類是 StandardWrapper,StandardWrapper 還實現了擁有一個 Servlet 初始化信息的 ServletConfig,由此看出 StandardWrapper 將直接和 Servlet 的各種信息打交道。

下面看一下非常重要的一個方法 loadServlet,代碼片段如下:

清單 16. StandardWrapper.loadServlet
public synchronized Servlet loadServlet() throws ServletException {………Servlet servlet;try {………ClassLoader classLoader = loader.getClassLoader();………Class classClass = null;………servlet = (Servlet) classClass.newInstance();if ((servlet instanceof ContainerServlet) &&(isContainerProvidedServlet(actualClass) ||((Context)getParent()).getPrivileged() )) {((ContainerServlet) servlet).setWrapper(this);}classLoadTime=(int) (System.currentTimeMillis() -t1);try {instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,servlet);if( System.getSecurityManager() != null) {Class[] classType = new Class[]{ServletConfig.class};Object[] args = new Object[]{((ServletConfig)facade)};SecurityUtil.doAsPrivilege("init",servlet,classType,args);} else {servlet.init(facade);}if ((loadOnStartup >= 0) && (jspFile != null)) {………if( System.getSecurityManager() != null) {Class[] classType = new Class[]{ServletRequest.class,ServletResponse.class};Object[] args = new Object[]{req, res};SecurityUtil.doAsPrivilege("service",servlet,classType,args);} else {servlet.service(req, res);}}instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,servlet);………return servlet; }

它基本上描述了對 Servlet 的操作,當裝載了 Servlet 后就會調用 Servlet 的 init 方法,同時會傳一個 StandardWrapperFacade 對象給 Servlet,這個對象包裝了 StandardWrapper,ServletConfig 與它們的關系圖如下:

圖 13. ServletConfig 與 StandardWrapperFacade、StandardWrapper 的關系

Servlet 可以獲得的信息都在 StandardWrapperFacade 封裝,這些信息又是在 StandardWrapper 對象中拿到的。所以 Servlet 可以通過 ServletConfig 拿到有限的容器的信息。

當 Servlet 被初始化完成后,就等著 StandardWrapperValve 去調用它的 service 方法了,調用 service 方法之前要調用 Servlet 所有的 filter。

?

轉載于:https://www.cnblogs.com/xbj-2016/p/5910817.html

總結

以上是生活随笔為你收集整理的Tomcat的系统架构(以Tomcat5为基础)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。