整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          談?wù)?Tomcat 請(qǐng)求處理流程

          談?wù)?Tomcat 請(qǐng)求處理流程
          • verview
          • Connector Init and Start
          • Request Process
          • Acceptor
          • Poller
          • Worker
          • Container
          • Reference

          建議結(jié)合《談?wù)?Tomcat 架構(gòu)及啟動(dòng)過(guò)程[含部署]》一起看!

          談?wù)?Tomcat 架構(gòu)及啟動(dòng)過(guò)程[含部署]

          http://www.importnew.com/27724.html

          很多東西在時(shí)序圖中體現(xiàn)的已經(jīng)非常清楚了,沒(méi)有必要再一步一步的作介紹,所以本文以圖為主,然后對(duì)部分內(nèi)容加以簡(jiǎn)單解釋。

          繪制圖形使用的工具是 PlantUML + Visual Studio Code + PlantUML Extension

          本文對(duì) Tomcat 的介紹以 Tomcat-9.0.0.M22 為標(biāo)準(zhǔn)。

          Tomcat-9.0.0.M22 是 Tomcat 目前最新的版本,但尚未發(fā)布,它實(shí)現(xiàn)了 Servlet4.0 及 JSP2.3 并提供了很多新特性,需要 1.8 及以上的 JDK 支持等等,詳情請(qǐng)查閱 Tomcat-9.0-doc。

          https://tomcat.apache.org/tomcat-9.0-doc/index.html

          Overview

          Connector 啟動(dòng)以后會(huì)啟動(dòng)一組線程用于不同階段的請(qǐng)求處理過(guò)程。

          1. Acceptor 線程組。用于接受新連接,并將新連接封裝一下,選擇一個(gè) Poller 將新連接添加到 Poller 的事件隊(duì)列中。
          2. Poller 線程組。用于監(jiān)聽(tīng) Socket 事件,當(dāng) Socket 可讀或可寫(xiě)等等時(shí),將 Socket 封裝一下添加到 worker 線程池的任務(wù)隊(duì)列中。
          3. worker 線程組。用于對(duì)請(qǐng)求進(jìn)行處理,包括分析請(qǐng)求報(bào)文并創(chuàng)建 Request 對(duì)象,調(diào)用容器的 pipeline 進(jìn)行處理。

          Acceptor、Poller、worker 所在的 ThreadPoolExecutor 都維護(hù)在 NioEndpoint 中。

          Connector Init and Start

          1. initServerSocket(),通過(guò) ServerSocketChannel.open() 打開(kāi)一個(gè) ServerSocket,默認(rèn)綁定到 8080 端口,默認(rèn)的連接等待隊(duì)列長(zhǎng)度是 100, 當(dāng)超過(guò) 100 個(gè)時(shí)會(huì)拒絕服務(wù)。我們可以通過(guò)配置 conf/server.xml 中 Connector 的 acceptCount 屬性對(duì)其進(jìn)行定制。
          2. createExecutor() 用于創(chuàng)建 Worker 線程池。默認(rèn)會(huì)啟動(dòng) 10 個(gè) Worker 線程,Tomcat 處理請(qǐng)求過(guò)程中,Woker 最多不超過(guò) 200 個(gè)。我們可以通過(guò)配置 conf/server.xml 中 Connector 的 minSpareThreads 和 maxThreads 對(duì)這兩個(gè)屬性進(jìn)行定制。
          3. Pollor 用于檢測(cè)已就緒的 Socket。默認(rèn)最多不超過(guò) 2 個(gè),Math.min(2,Runtime.getRuntime().availableProcessors());。我們可以通過(guò)配置 pollerThreadCount 來(lái)定制。
          4. Acceptor 用于接受新連接。默認(rèn)是 1 個(gè)。我們可以通過(guò)配置 acceptorThreadCount 對(duì)其進(jìn)行定制。

          Request Process

          Acceptor

          1. Acceptor 在啟動(dòng)后會(huì)阻塞在 ServerSocketChannel.accept(); 方法處,當(dāng)有新連接到達(dá)時(shí),該方法返回一個(gè) SocketChannel。
          2. 配置完 Socket 以后將 Socket 封裝到 NioChannel 中,并注冊(cè)到 Poller,值的一提的是,我們一開(kāi)始就啟動(dòng)了多個(gè) Poller 線程,注冊(cè)的時(shí)候,連接是公平的分配到每個(gè) Poller 的。NioEndpoint 維護(hù)了一個(gè) Poller 數(shù)組,當(dāng)一個(gè)連接分配給 pollers[index] 時(shí),下一個(gè)連接就會(huì)分配給 pollers[(index+1)%pollers.length].
          3. addEvent() 方法會(huì)將 Socket 添加到該 Poller 的 PollerEvent 隊(duì)列中。到此 Acceptor 的任務(wù)就完成了。

          Poller

          1. selector.select(1000)。當(dāng) Poller 啟動(dòng)后因?yàn)?selector 中并沒(méi)有已注冊(cè)的 Channel,所以當(dāng)執(zhí)行到該方法時(shí)只能阻塞。所有的 Poller 共用一個(gè) Selector,其實(shí)現(xiàn)類是 sun.nio.ch.EPollSelectorImpl
          2. events() 方法會(huì)將通過(guò) addEvent() 方法添加到事件隊(duì)列中的 Socket 注冊(cè)到 EPollSelectorImpl,當(dāng) Socket 可讀時(shí),Poller 才對(duì)其進(jìn)行處理
          3. createSocketProcessor() 方法將 Socket 封裝到 SocketProcessor 中,SocketProcessor 實(shí)現(xiàn)了 Runnable 接口。worker 線程通過(guò)調(diào)用其 run() 方法來(lái)對(duì) Socket 進(jìn)行處理。
          4. execute(SocketProcessor) 方法將 SocketProcessor 提交到線程池,放入線程池的 workQueue 中。workQueue 是 BlockingQueue 的實(shí)例。到此 Poller 的任務(wù)就完成了。

          Worker

          • worker 線程被創(chuàng)建以后就執(zhí)行 ThreadPoolExecutor 的 runWorker() 方法,試圖從 workQueue 中取待處理任務(wù),但是一開(kāi)始 workQueue 是空的,所以 worker 線程會(huì)阻塞在 workQueue.take() 方法。
          • 當(dāng)新任務(wù)添加到 workQueue后,workQueue.take() 方法會(huì)返回一個(gè) Runnable,通常是 SocketProcessor,然后 worker 線程調(diào)用 SocketProcessor 的 run() 方法對(duì) Socket 進(jìn)行處理。
          • createProcessor() 會(huì)創(chuàng)建一個(gè) Http11Processor, 它用來(lái)解析 Socket,將 Socket 中的內(nèi)容封裝到 Request 中。注意這個(gè) Request 是臨時(shí)使用的一個(gè)類,它的全類名是 org.apache.coyote.Request,
          • postParseRequest() 方法封裝一下 Request,并處理一下映射關(guān)系(從 URL 映射到相應(yīng)的 Host、Context、Wrapper)。
          1. CoyoteAdapter 將 Rquest 提交給 Container 處理之前,并將 org.apache.coyote.Request 封裝到 org.apache.catalina.connector.Request,傳遞給 Container 處理的 Request 是 org.apache.catalina.connector.Request。
          2. connector.getService().getMapper().map(),用來(lái)在 Mapper 中查詢 URL 的映射關(guān)系。映射關(guān)系會(huì)保留到 org.apache.catalina.connector.Request 中,Container 處理階段 request.getHost() 是使用的就是這個(gè)階段查詢到的映射主機(jī),以此類推 request.getContext()、request.getWrapper() 都是。
          • connector.getService().getContainer().getPipeline().getFirst().invoke() 會(huì)將請(qǐng)求傳遞到 Container 處理,當(dāng)然了 Container 處理也是在 Worker 線程中執(zhí)行的,但是這是一個(gè)相對(duì)獨(dú)立的模塊,所以單獨(dú)分出來(lái)一節(jié)。


          Container

          • 需要注意的是,基本上每一個(gè)容器的 StandardPipeline 上都會(huì)有多個(gè)已注冊(cè)的 Valve,我們只關(guān)注每個(gè)容器的 Basic Valve。其他 Valve 都是在 Basic Valve 前執(zhí)行。
          • request.getHost().getPipeline().getFirst().invoke() 先獲取對(duì)應(yīng)的 StandardHost,并執(zhí)行其 pipeline。
          • request.getContext().getPipeline().getFirst().invoke() 先獲取對(duì)應(yīng)的 StandardContext,并執(zhí)行其 pipeline。
          • request.getWrapper().getPipeline().getFirst().invoke() 先獲取對(duì)應(yīng)的 StandardWrapper,并執(zhí)行其 pipeline。
          • 最值得說(shuō)的就是 StandardWrapper 的 Basic Valve,StandardWrapperValve
          1. allocate() 用來(lái)加載并初始化 Servlet,值的一提的是 Servlet 并不都是單例的,當(dāng) Servlet 實(shí)現(xiàn)了 SingleThreadModel 接口后,StandardWrapper 會(huì)維護(hù)一組 Servlet 實(shí)例,這是享元模式。當(dāng)然了 SingleThreadModel在 Servlet 2.4 以后就棄用了。
          2. createFilterChain() 方法會(huì)從 StandardContext 中獲取到所有的過(guò)濾器,然后將匹配 Request URL 的所有過(guò)濾器挑選出來(lái)添加到 filterChain 中。
          3. doFilter() 執(zhí)行過(guò)濾鏈,當(dāng)所有的過(guò)濾器都執(zhí)行完畢后調(diào)用 Servlet 的 service() 方法。

          Reference

          1. 《How Tomcat works》
          2. https://www.amazon.com/How-Tomcat-Works-Budi-Kurniawan/dp/097521280X
          3. 《Tomcat 架構(gòu)解析》– 劉光瑞
          4. http://product.dangdang.com/25084132.html
          5. Tomcat-9.0-doc
          6. https://tomcat.apache.org/tomcat-9.0-doc/index.html
          7. apache-tomcat-9.0.0.M22-src
          8. http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.0.M22/src/
          9. tomcat架構(gòu)分析 (connector NIO 實(shí)現(xiàn))
          10. http://gearever.iteye.com/blog/1844203

          來(lái)源:http://rrd.me/ehmDB


          :-D 搜索微信號(hào)(ID:芋道源碼),可以獲得各種 Java 源碼解析、原理講解、面試題、學(xué)習(xí)指南。

          :-D 并且,回復(fù)【書(shū)籍】后,可以領(lǐng)取筆者推薦的各種 Java 從入門到架構(gòu)的 100 本書(shū)籍。

          :-D 并且,回復(fù)【技術(shù)群】后,可以加入專門討論 Java、后端、架構(gòu)的技術(shù)群。

          來(lái)吧,騷年~

          源:Rainstorm ,github.com/c-rainstorm/blog/blob/master/tomcat/談?wù)?20Tomcat%20架構(gòu)及啟動(dòng)過(guò)程%5B含部署%5D.md

          這個(gè)題目命的其實(shí)是很大的,寫(xiě)的時(shí)候還是很忐忑的,但我盡可能把這個(gè)過(guò)程描述清楚。因?yàn)檫@是讀過(guò)源碼以后寫(xiě)的總結(jié),在寫(xiě)的過(guò)程中可能會(huì)忽略一些前提條件,如果有哪些比較突兀就出現(xiàn),或不好理解的地方可以給我提 Issue,我會(huì)盡快補(bǔ)充修訂相關(guān)內(nèi)容。

          很多東西在時(shí)序圖中體現(xiàn)的已經(jīng)非常清楚了,沒(méi)有必要再一步一步的作介紹,所以本文以圖為主,然后對(duì)部分內(nèi)容加以簡(jiǎn)單解釋。

          繪制圖形使用的工具是 PlantUML + Visual Studio Code + PlantUML Extension

          圖形 PlantUML 源文件:

          • tomcat-architecture.pu
          • tomcat-init.pu
          • tomcat-start.pu
          • tomcat-context-start.pu
          • tomcat-background-thread.pu

          本文對(duì) Tomcat 的介紹以 Tomcat-9.0.0.M22 為標(biāo)準(zhǔn)。

          Tomcat-9.0.0.M22 是 Tomcat 目前最新的版本,但尚未發(fā)布,它實(shí)現(xiàn)了 Servlet4.0 及 JSP2.3 并提供了很多新特性,需要 1.8 及以上的 JDK 支持等等,詳情請(qǐng)查閱 Tomcat-9.0-doc

          Tomcat-9.0-dochttps://tomcat.apache.org/tomcat-9.0-doc/index.html

          Overview

          1. Bootstrap 作為 Tomcat 對(duì)外界的啟動(dòng)類,在 $CATALINA_BASE/bin 目錄下,它通過(guò)反射創(chuàng)建 Catalina 的實(shí)例并對(duì)其進(jìn)行初始化及啟動(dòng)。
          2. Catalina 解析 $CATALINA_BASE/conf/server.xml 文件并創(chuàng)建 StandardServer、StandardService、StandardEngine、StandardHost 等
          3. StandardServer 代表的是整個(gè) Servlet 容器,他包含一個(gè)或多個(gè) StandardService
          4. StandardService 包含一個(gè)或多個(gè) Connector,和一個(gè) Engine,Connector 和 Engine 都是在解析 conf/server.xml 文件時(shí)創(chuàng)建的,Engine 在 Tomcat 的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardEngine
          5. MapperListener 實(shí)現(xiàn)了 LifecycleListener 和 ContainerListener 接口用于監(jiān)聽(tīng)容器事件和生命周期事件。該監(jiān)聽(tīng)器實(shí)例監(jiān)聽(tīng)所有的容器,包括 StandardEngine、StandardHost、StandardContext、StandardWrapper,當(dāng)容器有變動(dòng)時(shí),注冊(cè)容器到 Mapper。
          6. Mapper 維護(hù)了 URL 到容器的映射關(guān)系。當(dāng)請(qǐng)求到來(lái)時(shí)會(huì)根據(jù) Mapper 中的映射信息決定將請(qǐng)求映射到哪一個(gè) Host、Context、Wrapper。
          7. Http11NioProtocol 用于處理 HTTP/1.1 的請(qǐng)求
          8. NioEndpoint 是連接的端點(diǎn),在請(qǐng)求處理流程中該類是核心類,會(huì)重點(diǎn)介紹。
          9. CoyoteAdapter 用于將請(qǐng)求從 Connctor 交給 Container 處理。使 Connctor 和 Container 解耦。
          10. StandardEngine 代表的是 Servlet 引擎,用于處理 Connector 接受的 Request。包含一個(gè)或多個(gè) Host(虛擬主機(jī)), Host 的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardHost。
          11. StandardHost 代表的是虛擬主機(jī),用于部署該虛擬主機(jī)上的應(yīng)用程序。通常包含多個(gè) Context (Context 在 Tomcat 中代表應(yīng)用程序)。Context 在 Tomcat 中的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardContext。
          12. StandardContext 代表一個(gè)獨(dú)立的應(yīng)用程序,通常包含多個(gè) Wrapper,一個(gè) Wrapper 容器封裝了一個(gè) Servlet,Wrapper的標(biāo)準(zhǔn)實(shí)現(xiàn)是 StandardWrapper。
          13. StandardPipeline 組件代表一個(gè)流水線,與 Valve(閥)結(jié)合,用于處理請(qǐng)求。 StandardPipeline 中含有多個(gè) Valve, 當(dāng)需要處理請(qǐng)求時(shí),會(huì)逐一調(diào)用 Valve 的 invoke 方法對(duì) Request 和 Response 進(jìn)行處理。特別的,其中有一個(gè)特殊的 Valve 叫 basicValve,每一個(gè)標(biāo)準(zhǔn)容器都有一個(gè)指定的 BasicValve,他們做的是最核心的工作。
          • StandardEngine 的是 StandardEngineValve,他用來(lái)將 Request 映射到指定的 Host;
          • StandardHost 的是 StandardHostValve, 他用來(lái)將 Request 映射到指定的 Context;
          • StandardContext 的是 StandardContextValve,它用來(lái)將 Request 映射到指定的 Wrapper;
          • StandardWrapper 的是 StandardWrapperValve,他用來(lái)加載 Rquest 所指定的 Servlet,并調(diào)用 Servlet 的 Service 方法。

          Tomcat init

          • 當(dāng)通過(guò) ./startup.sh 腳本或直接通過(guò) java 命令來(lái)啟動(dòng) Bootstrap 時(shí),Tomcat 的啟動(dòng)過(guò)程就正式開(kāi)始了,啟動(dòng)的入口點(diǎn)就是 Bootstrap 類的 main 方法。
          • 啟動(dòng)的過(guò)程分為兩步,分別是 init 和 start,本節(jié)主要介紹 init;
          • 初始化類加載器。[關(guān)于 Tomcat 類加載機(jī)制,可以參考我之前寫(xiě)的一片文章:談?wù)凧ava類加載機(jī)制]
          1. 通過(guò)從 CatalinaProperties 類中獲取 common.loader 等屬性,獲得類加載器的掃描倉(cāng)庫(kù)。CatalinaProperties 類在的靜態(tài)塊中調(diào)用了 loadProperties() 方法,從 conf/catalina.properties 文件中加載了屬性.(即在類創(chuàng)建的時(shí)候?qū)傩跃鸵呀?jīng)加載好了)。
          2. 通過(guò) ClassLoaderFactory 創(chuàng)建 URLClassLoader 的實(shí)例
          • 通過(guò)反射創(chuàng)建 Catalina 的實(shí)例并設(shè)置 parentClassLoader
          • setAwait(true)。設(shè)置 Catalina 的 await 屬性為 true。在 Start 階段尾部,若該屬性為 true,Tomcat 會(huì)在 main 線程中監(jiān)聽(tīng) SHUTDOWN 命令,默認(rèn)端口是 8005.當(dāng)收到該命令后執(zhí)行 Catalina 的 stop() 方法關(guān)閉 Tomcat 服務(wù)器。
          • createStartDigester()。Catalina 的該方法用于創(chuàng)建一個(gè) Digester 實(shí)例,并添加解析 conf/server.xml 的 RuleSet。Digester 原本是 Apache 的一個(gè)開(kāi)源項(xiàng)目,專門解析 XML 文件的,但我看 Tomcat-9.0.0.M22 中直接將這些類整合到 Tomcat 內(nèi)部了,而不是引入 jar 文件。Digester 工具的原理不在本文的介紹范圍,有興趣的話可以參考 The Digester Component – Apache 或 《How Tomcat works》- Digester [推薦] 一章
          • parse() 方法就是 Digester 處理 conf/server.xml 創(chuàng)建各個(gè)組件的過(guò)程。值的一提的是這些組件都是使用反射的方式來(lái)創(chuàng)建的。特別的,在創(chuàng)建 Digester 的時(shí)候,添加了一些特別的 rule Set,用于創(chuàng)建一些十分核心的組件,這些組件在 conf/server.xml 中沒(méi)有但是其作用都比較大,這里做下簡(jiǎn)單介紹,當(dāng) Start 時(shí)用到了再詳細(xì)說(shuō)明:
          1. EngineConfig。LifecycleListener 的實(shí)現(xiàn)類,觸發(fā) Engine 的生命周期事件后調(diào)用,這個(gè)監(jiān)聽(tīng)器沒(méi)有特別大的作用,就是打印一下日志
          2. HostConfig。LifecycleListener 的實(shí)現(xiàn)類,觸發(fā) Host 的生命周期事件后調(diào)用。這個(gè)監(jiān)聽(tīng)器的作用就是部署應(yīng)用程序,這包括 conf/<Engine>/<Host>/ 目錄下所有的 Context xml 文件 和 webapps 目錄下的應(yīng)用程序,不管是 war 文件還是已解壓的目錄。 另外后臺(tái)進(jìn)程對(duì)應(yīng)用程序的熱部署也是由該監(jiān)聽(tīng)器負(fù)責(zé)的。
          3. ContextConfig。LifecycleListener 的實(shí)現(xiàn)類,觸發(fā) Context 的生命周期事件時(shí)調(diào)用。這個(gè)監(jiān)聽(tīng)器的作用是配置應(yīng)用程序,它會(huì)讀取并合并 conf/web.xml 和 應(yīng)用程序的 web.xml,分析 /WEB-INF/classes/ 和 /WEB-INF/lib/*.jar中的 Class 文件的注解,將其中所有的 Servlet、ServletMapping、Filter、FilterMapping、Listener 都配置到 StandardContext 中,以備后期使用。當(dāng)然了 web.xml 中還有一些其他的應(yīng)用程序參數(shù),最后都會(huì)一并配置到 StandardContext 中。
          • reconfigureStartStopExecutor() 用于重新配置啟動(dòng)和停止子容器的 Executor。默認(rèn)是 1 個(gè)線程。我們可以配置 conf/server.xml 中 Engine 的 startStopThreads,來(lái)指定用于啟動(dòng)和停止子容器的線程數(shù)量,如果配置 0 的話會(huì)使用 Runtime.getRuntime().availableProcessors() 作為線程數(shù),若配置為負(fù)數(shù)的話會(huì)使用 Runtime.getRuntime().availableProcessors() + 配置值,若和小與 1 的話,使用 1 作為線程數(shù)。當(dāng)線程數(shù)是 1 時(shí),使用 InlineExecutorService 它直接使用當(dāng)前線程來(lái)執(zhí)行啟動(dòng)停止操作,否則使用 ThreadPoolExecutor 來(lái)執(zhí)行,其最大線程數(shù)為我們配置的值。
          • 需要注意的是 Host 的 init 操作是在 Start 階段來(lái)做的, StardardHost 創(chuàng)建好后其 state 屬性的默認(rèn)值是 LifecycleState.NEW,所以在其調(diào)用 startInternal() 之前會(huì)進(jìn)行一次初始化。

          Tomcat Start[Deployment]

          • 圖中從 StandardHost Start StandardContext 的這步其實(shí)在真正的執(zhí)行流程中會(huì)直接跳過(guò),因?yàn)?conf/server.xml 文件中并沒(méi)有配置任何的 Context,所以在 findChildren() 查找子容器時(shí)會(huì)返回空數(shù)組,所以之后遍歷子容器來(lái)啟動(dòng)子容器的 for 循環(huán)就直接跳過(guò)了。
          • 觸發(fā) Host 的 BEFORE_START_EVENT 生命周期事件,HostConfig 調(diào)用其 beforeStart() 方法創(chuàng)建 $CATALINA_BASE/webapps& $CATALINA_BASE/conf/<Engine>/<Host>/ 目錄。
          • 觸發(fā) Host 的 START_EVENT 生命周期事件,HostConfig 調(diào)用其 start() 方法開(kāi)始部署已在 $CATALINA_BASE/webapps & $CATALINA_BASE/conf/<Engine>/<Host>/ 目錄下的應(yīng)用程序。
          1. 解析 $CATALINA_BASE/conf/<Engine>/<Host>/ 目錄下所有定義 Context 的 XML 文件,并添加到 StandardHost。這些 XML 文件稱為應(yīng)用程序描述符。正因?yàn)槿绱耍覀兛梢耘渲靡粋€(gè)虛擬路徑來(lái)保存應(yīng)用程序中用到的圖片,詳細(xì)的配置過(guò)程請(qǐng)參考 開(kāi)發(fā)環(huán)境配置指南 – 6.3. 配置圖片存放目錄
          2. 部署 $CATALINA_BASE/webapps 下所有的 WAR 文件,并添加到 StandardHost。
          3. 部署 $CATALINA_BASE/webapps 下所有已解壓的目錄,并添加到 StandardHost。

          特別的,添加到 StandardHost 時(shí),會(huì)直接調(diào)用 StandardContext 的 start() 方法來(lái)啟動(dòng)應(yīng)用程序。啟動(dòng)應(yīng)用程序步驟請(qǐng)看 Context Start 一節(jié)。

          • 在 StandardEngine 和 StandardContext 啟動(dòng)時(shí)都會(huì)調(diào)用各自的 threadStart() 方法,該方法會(huì)創(chuàng)建一個(gè)新的后臺(tái)線程來(lái)處理該該容器和子容器及容器內(nèi)各組件的后臺(tái)事件。StandardEngine 會(huì)直接創(chuàng)建一個(gè)后臺(tái)線程,StandardContext 默認(rèn)是不創(chuàng)建的,和 StandardEngine 共用同一個(gè)。后臺(tái)線程處理機(jī)制是周期調(diào)用組件的 backgroundProcess() 方法。詳情請(qǐng)看 Background process 一節(jié)。
          • MapperListener
          1. addListeners(engine) 方法會(huì)將該監(jiān)聽(tīng)器添加到 StandardEngine 和它的所有子容器中
          2. registerHost() 會(huì)注冊(cè)所有的 Host 和他們的子容器到 Mapper 中,方便后期請(qǐng)求處理時(shí)使用。
          3. 當(dāng)有新的應(yīng)用(StandardContext)添加進(jìn)來(lái)后,會(huì)觸發(fā) Host 的容器事件,然后通過(guò) MapperListener 將新應(yīng)用的映射注冊(cè)到 Mapper 中。
          • Start 工作都做完以后 Catalina 會(huì)創(chuàng)建一個(gè) CatalinaShutdownHook 并注冊(cè)到 JVM。CatalinaShutdownHook 繼承了 Thread,是 Catalina 的內(nèi)部類。其 run 方法中直接調(diào)用了 Catalina 的 stop() 方法來(lái)關(guān)閉整個(gè)服務(wù)器。注冊(cè)該 Thread 到 JVM 的原因是防止用戶非正常終止 Tomcat,比如直接關(guān)閉命令窗口之類的。當(dāng)直接關(guān)閉命令窗口時(shí),操作系統(tǒng)會(huì)向 JVM 發(fā)送一個(gè)終止信號(hào),然后 JVM 在退出前會(huì)逐一啟動(dòng)已注冊(cè)的 ShutdownHook 來(lái)關(guān)閉相應(yīng)資源。

          Context Start

          • StandRoot 類實(shí)現(xiàn)了 WebResourceRoot 接口,它容納了一個(gè)應(yīng)用程序的所有資源,通俗的來(lái)說(shuō)就是部署到 webapps 目錄下對(duì)應(yīng) Context 的目錄里的所有資源。因?yàn)槲覍?duì) Tomcat 的資源管理部分暫時(shí)不是很感興趣,所以資源管理相關(guān)類只是做了簡(jiǎn)單了解,并沒(méi)有深入研究源代碼。
          • resourceStart() 方法會(huì)對(duì) StandardRoot 進(jìn)行初始配置
          • postWorkDirectory() 用于創(chuàng)建對(duì)應(yīng)的工作目錄 $CATALINA_BASE/work/<Engine>/<Host>/<Context>, 該目錄用于存放臨時(shí)文件。
          • StardardContext 只是一個(gè)容器,而 ApplicationContext 則是一個(gè)應(yīng)用程序真正的運(yùn)行環(huán)境,相關(guān)類及操作會(huì)在請(qǐng)求處理流程看完以后進(jìn)行補(bǔ)充。
          • StardardContext 觸發(fā) CONFIGURE_START_EVENT 生命周期事件,ContextConfig 開(kāi)始調(diào)用 configureStart() 對(duì)應(yīng)用程序進(jìn)行配置。
          1. 這個(gè)過(guò)程會(huì)解析并合并 conf/web.xml & conf/<Engine>/<Host>/web.xml.default & webapps/<Context>/WEB-INF/web.xml 中的配置。
          2. 配置配置文件中的參數(shù)到 StandardContext, 其中主要的包括 Servlet、Filter、Listener。
          3. 因?yàn)閺?Servlet3.0 以后是直接支持注解的,所以服務(wù)器必須能夠處理加了注解的類。Tomcat 通過(guò)分析 WEB-INF/classes/ 中的 Class 文件和 WEB-INF/lib/ 下的 jar 包將掃描到的 Servlet、Filter、Listerner 注冊(cè)到 StandardContext。
          4. setConfigured(true),是非常關(guān)鍵的一個(gè)操作,它標(biāo)識(shí)了 Context 的成功配置,若未設(shè)置該值為 true 的話,Context 會(huì)啟動(dòng)失敗。

          Background process

          • 后臺(tái)進(jìn)程的作用就是處理一下 Servlet 引擎中的周期性事件,處理周期默認(rèn)是 10s。
          • 特別的 StandardHost 的 backgroundProcess() 方法會(huì)觸發(fā) Host 的 PERIODIC_EVENT 生命周期事件。然后 HostConfig 會(huì)調(diào)用其 check() 方法對(duì)已加載并進(jìn)行過(guò)重新部署的應(yīng)用程序進(jìn)行 reload 或?qū)π虏渴鸬膽?yīng)用程序進(jìn)行熱部署。熱部署跟之前介紹的部署步驟一致, reload() 過(guò)程只是簡(jiǎn)單的順序調(diào)用 setPause(true)、stop()、start()、setPause(false),其中 setPause(true) 的作用是暫時(shí)停止接受請(qǐng)求。

          How to read excellent open source projects

          真正的第一次閱讀開(kāi)源項(xiàng)目源代碼,收獲還是很大的。讓我在架構(gòu)設(shè)計(jì)、面向?qū)ο笏枷搿⒃O(shè)計(jì)模式、Clean Code等等各個(gè)方面都有了進(jìn)步。閱讀優(yōu)秀的開(kāi)源項(xiàng)目其實(shí)是一件很爽的事,因?yàn)闀r(shí)不時(shí)的會(huì)發(fā)現(xiàn)一個(gè)新的設(shè)計(jì)思路,然后不由自主的感嘆一聲居然還可以這樣!當(dāng)然了,讀的時(shí)候還是會(huì)有一些痛點(diǎn)的,比如說(shuō)碰到一個(gè)變量,但是死活就是找不到初始化的位置,有時(shí)通過(guò) Find Usage 工具可以找到,但有些找不到的只能從頭開(kāi)始再過(guò)一邊源碼。有時(shí)碰到一個(gè)設(shè)計(jì)思路死活都想不明白為什么這樣設(shè)計(jì)等等,這種情況就只能通過(guò)分析更高一層的架構(gòu)來(lái)解決了等等。

          下面我簡(jiǎn)單分享一下我是如何閱讀開(kāi)源項(xiàng)目源碼的。

          • 先找一些介紹該項(xiàng)目架構(gòu)的書(shū)籍來(lái)看,項(xiàng)目架構(gòu)是項(xiàng)目核心中的核心,讀架構(gòu)讀的是高層次的設(shè)計(jì)思路,讀源碼讀的是低層次的實(shí)現(xiàn)細(xì)節(jié)。有了高層次的設(shè)計(jì)思路做指導(dǎo),源碼讀起來(lái)才會(huì)得心應(yīng)手,因?yàn)樽x的時(shí)候心里很清楚現(xiàn)在在讀的源碼在整個(gè)項(xiàng)目架構(gòu)中處于什么位置。我在讀 Tomcat 源碼之前先把 《How Tomcat works》 一書(shū)過(guò)了一邊,然后又看了一下 《Tomcat 架構(gòu)解析》 的第二章,對(duì) Tomcat 的架構(gòu)有了初步了解。(PS:《How Tomcat works》一書(shū)是全英文的,但讀起來(lái)非常流暢,雖然它是基于 Tomcat 4 和 5 的,但 Tomcat 架構(gòu)沒(méi)有非常大的變化,新版的 Tomcat 只是增加了一些組件,如果你要學(xué)習(xí) Tomcat 的話,首推這本書(shū)!)
          • 如果實(shí)在找不到講架構(gòu)的書(shū),那就自己動(dòng)手畫(huà)類圖吧!一般來(lái)說(shuō),開(kāi)源項(xiàng)目都是為了提供服務(wù)的,我們把提供服務(wù)的流程作為主線來(lái)分析源代碼,這樣目的性會(huì)更強(qiáng)一些,將該流程中涉及到的類畫(huà)到類圖中,最后得到的類圖就是架構(gòu)!不過(guò)分析之前你要先找到流程的入口點(diǎn),否則分析就無(wú)從開(kāi)始。以 Tomcat 為例,他的主線流程大致可以分為 3 個(gè):?jiǎn)?dòng)、部署、請(qǐng)求處理。他們的入口點(diǎn)就是 Bootstrap 類和 接受請(qǐng)求的 Acceptor 類!
          • 有了閱讀思路我們下面來(lái)說(shuō)說(shuō)工具吧。我使用的閱讀工具是 IntelliJ IDEA,一款十分強(qiáng)大的 IDE,可能比較重量級(jí),如果你有其他更加輕量級(jí)的 Linux 平臺(tái)源碼閱讀工具,可以推薦給我~
          1. Structure 欄目可以自定義列出類中的域、方法,然后還可以按照繼承結(jié)構(gòu)對(duì)域和方法進(jìn)行分組,這樣就可以直接看出來(lái)域和方法是在繼承結(jié)構(gòu)中哪個(gè)類里定義的。當(dāng)你點(diǎn)擊方法和域時(shí),還可以自動(dòng)滾動(dòng)到源代碼等等。
          2. 在源代碼中 點(diǎn)擊右鍵 -> Diagrams -> show Diagram 可以顯示類的繼承結(jié)構(gòu),圖中包含了該類所有的祖先和所有的接口。在該圖中選擇指定的父類和接口,點(diǎn)擊右鍵 -> show Implementations, IDEA 會(huì)列出接口的實(shí)現(xiàn)類或該類的子類。
          3. FindUsage、Go To Declaration 等等就不再多說(shuō)了。
          4. 目前想到的就這么多,如果你發(fā)現(xiàn)了我沒(méi)有提到的功能,歡迎跟我郵件交流~

          Reference

          1. 《How Tomcat works》
          2. https://www.amazon.com/How-Tomcat-Works-Budi-Kurniawan/dp/097521280X
          3. 《Tomcat 架構(gòu)解析》– 劉光瑞
          4. http://product.dangdang.com/25084132.html
          5. Tomcat-9.0-doc
          6. https://tomcat.apache.org/tomcat-9.0-doc/index.html
          7. apache-tomcat-9.0.0.M22-src
          8. http://www-eu.apache.org/dist/tomcat/tomcat-9/v9.0.0.M22/src/

          直都在做App開(kāi)發(fā),但是對(duì)java web方面的了解比較少,最近有時(shí)間,所以自己就尋思動(dòng)手從零開(kāi)始搭建一個(gè)java web項(xiàng)目。該項(xiàng)目主要是給app,提供接口服務(wù),簡(jiǎn)稱項(xiàng)目為‘mserver’。好了,接下來(lái),我們就開(kāi)始簡(jiǎn)單的搭建項(xiàng)目。

          一.開(kāi)發(fā)所需工具。(根據(jù)你的電腦以及系統(tǒng)選擇合適的版本下載)

          1.JDK 。下載鏈接。

          JDK(Java Development Kit) 是 Java 語(yǔ)言的軟件開(kāi)發(fā)工具包(SDK)。JDK是 Java 語(yǔ)言的軟件開(kāi)發(fā)工具包,主要用于移動(dòng)設(shè)備、嵌入式設(shè)備上的java應(yīng)用程序。JDK是整個(gè)java開(kāi)發(fā)的核心,它包含了JAVA的運(yùn)行環(huán)境,JAVA工具和JAVA基礎(chǔ)的類庫(kù)。

          2.eclipse開(kāi)發(fā)工具。下載鏈接。

          PS:我們?cè)谙螺d軟件的過(guò)程中,可能會(huì)有多個(gè)不同的下載文件,例如,zip、msi等,那么這兩個(gè)有什么區(qū)別嗎?


          1. ZIP是免安裝的軟件包,MSI是微軟特有壓縮格式,是需要安裝的軟件包
          2. ZIP Archive: 壓縮版本,需要自己配置
          3. MSI Installer:安裝版本,安裝過(guò)程中自動(dòng)配置

          3.Tomcat服務(wù)器。下載鏈接。

          4.Mysq數(shù)據(jù)庫(kù)。下載鏈接 。

          如果你不習(xí)慣使用命令去操作數(shù)據(jù)庫(kù),那么推薦你使用SQLyog工具,下載鏈接。

          PS:下載安裝好JDK 后,需要配置環(huán)境變量。(以Win7電腦為例,配置環(huán)境變量)

          (1).依次點(diǎn)擊,計(jì)算機(jī)→屬性→高級(jí)系統(tǒng)設(shè)置→高級(jí)→環(huán)境變量;

          (2).打開(kāi)系統(tǒng)變量,新建JAVA_HOME 變量,變量值填寫(xiě)jdk的安裝目錄(本人是 D:\Java\jdk1.7.0);

          (3).在系統(tǒng)變量中找到Path變量,點(diǎn)擊編輯,

          在變量值最后輸入 %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;

          (注意原來(lái)Path的變量值末尾如果沒(méi)有‘;’,先輸入';'后再輸入上面的代碼);

          (4).在系統(tǒng)變量中新建 CLASSPATH 變量

          變量值填寫(xiě) .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar(注意最前面有一點(diǎn));

          經(jīng)過(guò)以上幾步,環(huán)境變量就配置完成了。但是,我們還得驗(yàn)證一下是否配置成功,運(yùn)行cmd 輸入 java -version,如出現(xiàn)以下截圖顯示的顯示,說(shuō)明配置成功。

          二. 創(chuàng)建項(xiàng)目。

          1.打開(kāi)eclipse,先配置一下Tomcat服務(wù)器。

          (1).打開(kāi)windows-> preferences,找到Server下方的Runtime Environment,單擊右方的Add按鈕:

          (2). 選擇已經(jīng)安裝的Tomcat版本,點(diǎn)擊Next,

          (3).找到下載安裝后的Tomcat,點(diǎn)擊Finish。

          經(jīng)過(guò)以上幾步,就可以看到,紅色框中出現(xiàn)剛才你添加的Tomcat服務(wù)器。

          2.創(chuàng)建項(xiàng)目。

          (1). 打開(kāi)eclipse,在Workspace空白的地方,右鍵,New,選擇‘Dynamic Web project’,點(diǎn)擊Next,

          (2).輸入項(xiàng)目名稱,選擇服務(wù)器,其他的默認(rèn)就可以,點(diǎn)擊Finish就可以了!

          稍等一會(huì),你的Eclipse中會(huì)出現(xiàn)兩個(gè)項(xiàng)目,如下圖所示,表示創(chuàng)建項(xiàng)目成功了。

          此時(shí),你可以'run'項(xiàng)目,運(yùn)行后,可能會(huì)報(bào)404,這是因?yàn)槟愕膚eb項(xiàng)目底下沒(méi)有可訪問(wèn)的資源。那么就在項(xiàng)目的‘WebContent’目錄底下,創(chuàng)建一個(gè)比較簡(jiǎn)單的jsp文件index.jsp,具體代碼,如下


          1. <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
          2. pageEncoding="ISO-8859-1"%>
          3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
          4. <html>
          5. <head>
          6. <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
          7. <title>Insert title here</title>
          8. </head>
          9. <body>
          10. <h1>hello world!</h1>
          11. <h2>This is my test page!</h2>
          12. <h3>Welcome to my page!</h3>
          13. </body>
          14. </html>

          創(chuàng)建成功后,點(diǎn)擊運(yùn)行,然后在瀏覽器中輸入“http://localhost:8080/mServer/index.jsp”,便可以看到,

          經(jīng)過(guò)上面兩大步驟,我們的mServer接口項(xiàng)目已經(jīng)創(chuàng)建成功了,我們就可以開(kāi)始進(jìn)行接口開(kāi)發(fā)了!


          主站蜘蛛池模板: 中文字幕精品一区二区| 中文字幕无线码一区二区 | 激情综合一区二区三区| 国产乱码精品一区二区三区中| 国产一区二区久久久| 农村人乱弄一区二区| 国产裸体舞一区二区三区| 亚洲av无码一区二区三区不卡| 97久久精品无码一区二区天美 | 亚洲一区二区三区无码国产 | 激情内射亚洲一区二区三区爱妻| 成人中文字幕一区二区三区| 日韩精品人妻一区二区中文八零| 性色AV一区二区三区| 国产传媒一区二区三区呀| 国产成人综合一区精品| 麻豆一区二区在我观看| 波多野结衣AV无码久久一区| 国产av熟女一区二区三区| 国偷自产一区二区免费视频| 乱色熟女综合一区二区三区| 亚洲欧美国产国产一区二区三区| 精品一区精品二区| 亚洲av无码片vr一区二区三区 | 亚洲一区无码精品色| 亚洲欧洲精品一区二区三区| 日韩一区二区三区免费体验| 亚洲综合av永久无码精品一区二区 | 国产精品被窝福利一区| 精品动漫一区二区无遮挡| 一区二区三区国模大胆| 亚洲一区二区三区四区视频| 亚洲欧洲一区二区三区| 色一情一乱一伦一区二区三欧美 | 91福利视频一区| 免费人妻精品一区二区三区| 久久er99热精品一区二区| 国产精品免费大片一区二区| 国产精品av一区二区三区不卡蜜 | 无码国产精品一区二区免费虚拟VR | 国产91一区二区在线播放不卡|