Tomcat熱部署與熱加載


Adblocker
 
 
 
 

Tomcat熱部署與熱加載

 

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

熱部署表示重新部署應用,它的執行主體是Host,表示主機。

熱加載表示重新加載class,它的執行主體是Context,表示應用。

 

Tomcat中的后台線程

熱部署和熱加載都需要監聽相應的文件或文件夾是否發生了變化。它們都是由Tomcat的后台線程觸發的。

 

BackgroundProcessor就表示后台線程。

 

每個容器都可以擁有一個BackgroundProcessor,但是默認情況下只有Engine容器會在啟動的時候啟動一個BackgroundProcessor線程。

 

該線程會每隔一段時間(可以設置,單位為秒),去執行后台任務,先執行本容器定義的后台任務,然后再執行子容器的定義的后台任務,子容器的任務執行完成后會繼續執行其子容器的任務,直到沒有子容器為止。從這里可以看出就算每個容器自己開啟一個BackgroundProcessor,也只不過是多了一個執行相同任務的線程而已,執行任務的效率有所提升。

 

對於后台任務,所有容器會有一些統一的任務需要執行:

  1. 集群服務器心跳
  2. 如果一個容器擁有自己的類加載器,那么查看是否需要進行熱加載
  3. 檢查Session是否過期
  4. 執行每個容器對於的Realm對應的后台任務
  5. 執行每個容器中pipeline中的每個valve的后台任務
  6. 發布PERIODIC_EVENT事件

 

在這個過程中的第2步中會觸發熱加載第6步中會觸發熱部署

 

 

熱加載

我們可以在Context上配置reloadable屬性為true,這樣就表示該應用開啟了熱加載功能,默認是false。

 

熱加載觸發的條件是:WEB-INF/classes目錄下的文件發生了變化,WEB-INF/lib目錄下的jar包添加、刪除、修改都會觸發熱加載。

 

熱加載大致流程為:

  1. 設置當前Context不能接受以及處理請求標志為true
  2. 停止當前Context
  3. 啟動當前Context
  4. 設置當前Context不能接受以及處理請求標志為false

 

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

 

我們不妨先來分析第3步-啟動當前Context的過程中會發生什么事情:

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

 

加載類過程:

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

 

這是第3步,我們在來看第2步:

對於第2步-停止當前Context,其實所做的事情比較單一,就是清空和銷毀,而其中跟類加載相關就是清空上文中的緩存對象。

 

這樣,我們的熱加載就是先清空所有東西,然后重新啟動我們應用,但是因為這個的觸發條件基本上是class類發生了變化,所以熱加載的過程中關於應用其他的一些屬性是沒有發生變化的,比如你現在想在Context中添加一個Vavle是不會觸發熱加載的,而如果要達到這個效果就要用到熱部署

 

注意:雖然我們在熱加載的過程發現它是先停止再啟動,做法看似粗暴,但是這樣是性價比比較高的,並且這種方式至少比重啟Tomcat效率要高很多。

 

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

 

關於類的加載,這里有一點是需要注意的,對於一個class文件所表示的類,同一個類加載器的不同實例,都可以加載這個類,並且得到的class對象是不同的,回到熱加載,我們舉一個例子,我們現在有一個A類,一個自定義的WebappClassloader類,一開始先用一個WebappClassloader實例加載A類,那么在jvm中就會存在一個A類的class對象,然后進行熱加載,先停止,再啟動,在停止的時候會殺掉當前應用的所有線程(除開真正執行代碼的線程),再啟動時又會生成一個WebappClassloader實例來加載A類,如果熱加載之前的那個A類的class對象還沒有被回收的話,那么此時jvm中其實會存在兩個A類的class對象,這是不沖突,因為class對象的唯一標志是類加載器實例對象+類的全限定名

 

熱部署

BackgroundProcessor線程第六步會發出一個PERIODIC_EVENT事件,而HostConfig監聽了此事件,當接收到此事件后就會執行熱部署的檢查與操作。

 

對於一個文件夾部署的應用,通常會檢查以下資源是否發生變動:

  • /tomcat-7/webapps/應用名.war
  • /tomcat-7/webapps/應用名
  • /tomcat-7/webapps/應用名/META-INF/context.xml
  • /tomcat-7/conf/Catalina/localhost/應用名.xml
  • /tomcat-7/conf/context.xml

 

對於一個War部署的應用,會檢查以下資源是否發生變動:

  • /tomcat-7/webapps/應用名.war
  • /tomcat-7/conf/Catalina/localhost/應用名.xml
  • /tomcat-7/conf/context.xml

 

對於一個描述符部署的應用,會檢查以下資源是否發生變動:

  • /tomcat-7/conf/Catalina/localhost/應用名.xml
  • 指定的DocBase目錄
  • /tomcat-7/conf/context.xml

 

一旦這些文件或目錄發生了變化,就會觸發熱部署,當然熱部署也是有開關的,在Host上,默認是開啟的。這里需要注意的是,對於一個目錄是否發生了變化,Tomcat只判斷了這個目錄的修改時間是否發生了變化,所以和熱加載是不沖突的,因為熱加載監聽的是WEB-INF/classes和WEB-INF/lib目錄,而熱部署監聽的是應用名那一層的目錄。

 

 

在講熱部署的過程之前,我們要先講一下應用部署的優先級,對於一個應用,我們可以在四個地方進行定義:

  1. server.xml中的context節點
  2. /tomcat-7/conf/Catalina/localhost/應用名.xml
  3. /tomcat-7/webapps/應用名.war
  4. /tomcat-7/webapps/應用名

 

優先級就是上面所列的順序,意思是同一個應用名,如果你在這個四個地方都配置了,那么優先級低的將不起作用。因為Tomcat在部署一個應用的時候,會先查一下這個應用名是否已經被部署過了。

 

熱部署的過程:

如果發生改變的是文件夾,比如/tomcat-7/webapps/應用名,那么不會做什么事情,只是會更新一下記錄的修改時間,這是因為這個/tomcat-7/webapps/應用名目錄下的文件,要么是jsp文件,要么是其他文件,而Tomcat只會管jsp文件,而對於jsp文件如果發生了修改,jsp自帶的機制會處理修改的。

 

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

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM