Tomcat熱部署與熱加載

熱部署和熱加載是類似的,都是在不重啟Tomcat的情況下,使得應(yīng)用的最新代碼生效。

熱部署表示重新部署應(yīng)用,它的執(zhí)行主體是Host,表示主機(jī)。

熱加載表示重新加載class,它的執(zhí)行主體是Context,表示應(yīng)用。

Tomcat中的后臺(tái)線程

熱部署和熱加載都需要監(jiān)聽相應(yīng)的文件或文件夾是否發(fā)生了變化。它們都是由Tomcat的后臺(tái)線程觸發(fā)的。

BackgroundProcessor就表示后臺(tái)線程。

每個(gè)容器都可以擁有一個(gè)BackgroundProcessor,但是默認(rèn)情況下只有Engine容器會(huì)在啟動(dòng)的時(shí)候啟動(dòng)一個(gè)BackgroundProcessor線程。

該線程會(huì)每隔一段時(shí)間(可以設(shè)置,單位為秒),去執(zhí)行后臺(tái)任務(wù),先執(zhí)行本容器定義的后臺(tái)任務(wù),然后再執(zhí)行子容器的定義的后臺(tái)任務(wù),子容器的任務(wù)執(zhí)行完成后會(huì)繼續(xù)執(zhí)行其子容器的任務(wù),直到?jīng)]有子容器為止。從這里可以看出就算每個(gè)容器自己開啟一個(gè)BackgroundProcessor,也只不過是多了一個(gè)執(zhí)行相同任務(wù)的線程而已,執(zhí)行任務(wù)的效率有所提升。

對(duì)于后臺(tái)任務(wù),所有容器會(huì)有一些統(tǒng)一的任務(wù)需要執(zhí)行:

  1. 集群服務(wù)器心跳
  2. 如果一個(gè)容器擁有自己的類加載器,那么查看是否需要進(jìn)行熱加載
  3. 檢查Session是否過期
  4. 執(zhí)行每個(gè)容器對(duì)于的Realm對(duì)應(yīng)的后臺(tái)任務(wù)
  5. 執(zhí)行每個(gè)容器中pipeline中的每個(gè)valve的后臺(tái)任務(wù)
  6. 發(fā)布PERIODIC_EVENT事件
    在這個(gè)過程中的第2步中會(huì)觸發(fā)熱加載,第6步中會(huì)觸發(fā)熱部署

熱加載

我們可以在Context上配置reloadable屬性為true,這樣就表示該應(yīng)用開啟了熱加載功能,默認(rèn)是false。
熱加載觸發(fā)的條件是:WEB-INF/classes目錄下的文件發(fā)生了變化,WEB-INF/lib目錄下的jar包添加、刪除、修改都會(huì)觸發(fā)熱加載。

熱加載大致流程為:

  1. 設(shè)置當(dāng)前Context不能接受以及處理請(qǐng)求標(biāo)志為true
  2. 停止當(dāng)前Context
  3. 啟動(dòng)當(dāng)前Context
  4. 設(shè)置當(dāng)前Context不能接受以及處理請(qǐng)求標(biāo)志為false

我們著重來分析一下第2、3步。

我們不妨先來分析第3步-啟動(dòng)當(dāng)前Context的過程中會(huì)發(fā)生什么事情:

  1. 創(chuàng)建一個(gè)每個(gè)應(yīng)用都單獨(dú)自定義的WebappClassLoader
  2. 解析web.xml文件,這一步會(huì)做很多事情,但是主要的目的是尋找定義的Servlet并把它添加到Context中去,而對(duì)于尋找Servlet需要進(jìn)行兩個(gè)方面的尋找,一是從web.xml中尋找定義的Servlet,二是從尋找class文件中添加了@WebServlet注解的類。大家很有可能認(rèn)為,此時(shí)是不是會(huì)去加載我們定義的Servlet類,可以告訴大家的是,這個(gè)時(shí)候不會(huì),Servlet類的加載是在后面步驟發(fā)生的,那么這里就有疑問了,我們要看一個(gè)類上是不是存在一個(gè)@WebServlet注解,應(yīng)該要先加載這個(gè)類呀?Tomcat并沒有這么做,它是直接先把class文件當(dāng)做一個(gè)普通文件,然后看這個(gè)文件對(duì)應(yīng)的地方是否存在一個(gè)WebServlet注解,如果存在,則認(rèn)為這個(gè)class文件是一個(gè)Servlet,然后把這個(gè)class的全名封裝到Servlet對(duì)象中去,然后將Servlet對(duì)象添加到Context對(duì)象中。在解析web.xml時(shí)也是類似了,對(duì)于我們定義的Servlet,最后都會(huì)生成一個(gè)Servlet對(duì)象,然后記錄一個(gè)這個(gè)Servlet對(duì)象對(duì)應(yīng)的class的全名,最后把Servlet對(duì)象添加到Context中去。
  3. 我們?cè)谑褂肧ervlet的時(shí)候還會(huì)用其他的一些注解比如@ServletSecurity、@RunAs等等,對(duì)于這些注解是有特定功能的,Tomcat為了識(shí)別這個(gè)注解,此時(shí)就要去真正加載我們的Servlet類了。當(dāng)然要不要識(shí)別這些注解是可以配置的,如果不識(shí)別,那么這一步就不會(huì)發(fā)生了,那么Servlet類的加載就會(huì)在有請(qǐng)求過來時(shí)才會(huì)進(jìn)行類的加載。

加載類過程:

  1. 調(diào)用WebappClassLoaderBase的loadClass方法進(jìn)行類的加載,該方法傳遞一個(gè)類的全限定名。
  2. 要加載一個(gè)類,先得找到這個(gè)類在哪里,對(duì)應(yīng)的是哪個(gè)classs文件,所以Tomcat中有一個(gè)緩存對(duì)象,該對(duì)象保存了一個(gè)類的全限定名對(duì)應(yīng)的資源路徑。當(dāng)然,在第一次加載這個(gè)類時(shí),這個(gè)緩存是空的,所以這個(gè)時(shí)候就要去尋找這個(gè)類對(duì)應(yīng)的class文件地址,找到之后再緩存。接下來就來分析是怎么找到這個(gè)class文件地址的。
  3. 其實(shí)查找很容易,現(xiàn)在WEB-INF/classes/目錄下是否存在這個(gè)類,如果不存在就看WEB-INF/lib/目錄下的JAR包中是否存在這個(gè)類,最終如果找到就將進(jìn)行緩存,保存一個(gè)類的全限定名對(duì)應(yīng)的class文件地址或jar包地址。
  4. 當(dāng)知道這個(gè)類在哪了之后,就可以defineClass了,最終得到一個(gè)class對(duì)象,并且也會(huì)將這個(gè)class對(duì)象設(shè)置到我們的緩存中,所以上文說的緩存中,其實(shí)是這么一個(gè)映射關(guān)系,一個(gè)類的全限定名對(duì)應(yīng)這個(gè)類的文件地址以及這個(gè)類的class對(duì)象。
  5. 所以當(dāng)下次再有情況需要加載class時(shí),就可以直接取緩存中的對(duì)應(yīng)的class對(duì)象了。

這是第3步,我們?cè)趤砜吹?步:

對(duì)于第2步-停止當(dāng)前Context,其實(shí)所做的事情比較單一,就是清空和銷毀,而其中跟類加載相關(guān)就是清空上文中的緩存對(duì)象。
這樣,我們的熱加載就是先清空所有東西,然后重新啟動(dòng)我們應(yīng)用,但是因?yàn)檫@個(gè)的觸發(fā)條件基本上是class類發(fā)生了變化,所以熱加載的過程中關(guān)于應(yīng)用其他的一些屬性是沒有發(fā)生變化的,比如你現(xiàn)在想在Context中添加一個(gè)Vavle是不會(huì)觸發(fā)熱加載的,而如果要達(dá)到這個(gè)效果就要用到熱部署。

注意:雖然我們?cè)跓峒虞d的過程發(fā)現(xiàn)它是先停止再啟動(dòng),做法看似粗暴,但是這樣是性價(jià)比比較高的,并且這種方式至少比重啟Tomcat效率要高很多。

注意:熱加載不能用于war包

關(guān)于類的加載,這里有一點(diǎn)是需要注意的,對(duì)于一個(gè)class文件所表示的類,同一個(gè)類加載器的不同實(shí)例,都可以加載這個(gè)類,并且得到的class對(duì)象是不同的,回到熱加載,我們舉一個(gè)例子,我們現(xiàn)在有一個(gè)A類,一個(gè)自定義的WebappClassloader類,一開始先用一個(gè)WebappClassloader實(shí)例加載A類,那么在jvm中就會(huì)存在一個(gè)A類的class對(duì)象,然后進(jìn)行熱加載,先停止,再啟動(dòng),在停止的時(shí)候會(huì)殺掉當(dāng)前應(yīng)用的所有線程(除開真正執(zhí)行代碼的線程),再啟動(dòng)時(shí)又會(huì)生成一個(gè)WebappClassloader實(shí)例來加載A類,如果熱加載之前的那個(gè)A類的class對(duì)象還沒有被回收的話,那么此時(shí)jvm中其實(shí)會(huì)存在兩個(gè)A類的class對(duì)象,這是不沖突,因?yàn)閏lass對(duì)象的唯一標(biāo)志是類加載器實(shí)例對(duì)象+類的全限定名。

熱部署

BackgroundProcessor線程第六步會(huì)發(fā)出一個(gè)PERIODIC_EVENT事件,而HostConfig監(jiān)聽了此事件,當(dāng)接收到此事件后就會(huì)執(zhí)行熱部署的檢查與操作。

對(duì)于一個(gè)文件夾部署的應(yīng)用,通常會(huì)檢查以下資源是否發(fā)生變動(dòng):

? /tomcat-7/webapps/應(yīng)用名.war

? /tomcat-7/webapps/應(yīng)用名

? /tomcat-7/webapps/應(yīng)用名/META-INF/context.xml

? /tomcat-7/conf/Catalina/localhost/應(yīng)用名.xml

? /tomcat-7/conf/context.xml

對(duì)于一個(gè)War部署的應(yīng)用,會(huì)檢查以下資源是否發(fā)生變動(dòng):

? /tomcat-7/webapps/應(yīng)用名.war

? /tomcat-7/conf/Catalina/localhost/應(yīng)用名.xml

? /tomcat-7/conf/context.xml

對(duì)于一個(gè)描述符部署的應(yīng)用,會(huì)檢查以下資源是否發(fā)生變動(dòng):

? /tomcat-7/conf/Catalina/localhost/應(yīng)用名.xml

? 指定的DocBase目錄

? /tomcat-7/conf/context.xml

一旦這些文件或目錄發(fā)生了變化,就會(huì)觸發(fā)熱部署,當(dāng)然熱部署也是有開關(guān)的,在Host上,默認(rèn)是開啟的。這里需要注意的是,對(duì)于一個(gè)目錄是否發(fā)生了變化,Tomcat只判斷了這個(gè)目錄的修改時(shí)間是否發(fā)生了變化,所以和熱加載是不沖突的,因?yàn)闊峒虞d監(jiān)聽的是WEB-INF/classes和WEB-INF/lib目錄,而熱部署監(jiān)聽的是應(yīng)用名那一層的目錄。

在講熱部署的過程之前,我們要先講一下應(yīng)用部署的優(yōu)先級(jí),對(duì)于一個(gè)應(yīng)用,我們可以在四個(gè)地方進(jìn)行定義:

  1. server.xml中的context節(jié)點(diǎn)
  2. /tomcat-7/conf/Catalina/localhost/應(yīng)用名.xml
  3. /tomcat-7/webapps/應(yīng)用名.war
  4. /tomcat-7/webapps/應(yīng)用名

優(yōu)先級(jí)就是上面所列的順序,意思是同一個(gè)應(yīng)用名,如果你在這個(gè)四個(gè)地方都配置了,那么優(yōu)先級(jí)低的將不起作用。因?yàn)門omcat在部署一個(gè)應(yīng)用的時(shí)候,會(huì)先查一下這應(yīng)用名是否已經(jīng)被部署過了。

熱部署的過程:

如果發(fā)生改變的是文件夾,比如/tomcat-7/webapps/應(yīng)用名,那么不會(huì)做什么事情,只是會(huì)更新一下記錄的修改時(shí)間,這是因?yàn)檫@個(gè)/tomcat-7/webapps/應(yīng)用名目錄下的文件,要么是jsp文件,要么是其他文件,而Tomcat只會(huì)管jsp文件,而對(duì)于jsp文件如果發(fā)生了修改,jsp自帶的機(jī)制會(huì)處理修改的。

如果發(fā)生改變的是/tomcat-7/conf/Catalina/localhost/應(yīng)用名.xml文件,那么就是先undeploy,然后再deploy,和熱加載其實(shí)類似。對(duì)于undeploy就不多說了,就是講當(dāng)前應(yīng)用從host從移除,這就包括了當(dāng)前應(yīng)用的停止和銷毀,然后還會(huì)從已部署列表中移除當(dāng)前應(yīng)用,然后調(diào)用deployApps()就可以重新部署應(yīng)用了。

本文由博客一文多發(fā)平臺(tái) OpenWrite 發(fā)布!

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

相關(guān)閱讀更多精彩內(nèi)容

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