How Tomcat works — 七、tomcat發佈webapp

来源:http://www.cnblogs.com/sunshine-2015/archive/2016/08/14/5770556.html
-Advertisement-
Play Games

目錄 什麼叫發佈 webapp發佈方式 reload 總結 什麼叫發佈 發佈就是讓tomcat知道我們的程式在哪裡,並根據我們的配置創建Context,進行初始化、啟動,如下: 程式所在的位置 創建Context,添加到Host 初始化(創建解析webxml的digester) 啟動(初始化filt ...


目錄

  • 什麼叫發佈
  • webapp發佈方式
  • reload
  • 總結

什麼叫發佈

發佈就是讓tomcat知道我們的程式在哪裡,並根據我們的配置創建Context,進行初始化、啟動,如下:

  • 程式所在的位置
  • 創建Context,添加到Host
  • 初始化(創建解析webxml的digester)
  • 啟動(初始化filter、listener、servlet)

webapp發佈方式

在tomcat 中發佈webapp的方式不同會導致app啟動的先後順序不一樣(這裡按照啟動順序或者時機不同進行劃分):

  1. 在xml中配置,在conf/server.xml中配置(在host標簽內部)
  2. 直接將webapp文件夾、war包放在tomcat下麵的"webapps"目錄,或者在webapps下麵新建一個xml(根標簽為context,在屬性中表明應用程式的位置)

其實兩種發佈方式只是在啟動順序上稍有不同,啟動過程完全一致,先以在server.xml中配置說明

在server.xml進行配置

這情況在我們使用eclipse等工具進行開發發佈的時候,eclipse會幫我們在server.xml的Host標簽內添加Context,如下:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
--><!-- Note:  A "Server" is not itself a "Container", so you may not
     define subcomponents such as "Valves" at this level.
     Documentation at /docs/config/server.html
 --><Server port="8006" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener"/>
  <!-- Security listener. Documentation at /docs/config/listeners.html
  <Listener className="org.apache.catalina.security.SecurityListener" />
  -->
  <!--APR library loader. Documentation at /docs/apr.html -->
  <Listener SSLEngine="on" className="org.apache.catalina.core.AprLifecycleListener"/>
  <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
  <Listener className="org.apache.catalina.core.JasperListener"/>
  <!-- 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"/>

  <!-- Global JNDI resources
       Documentation at /docs/jndi-resources-howto.html
  -->
  <GlobalNamingResources>
    <!-- Editable user database that can also be used by
         UserDatabaseRealm to authenticate users
    -->
    <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase"/>
  </GlobalNamingResources>

  <!-- A "Service" is a collection of one or more "Connectors" that share
       a single "Container" Note:  A "Service" is not itself a "Container",
       so you may not define subcomponents such as "Valves" at this level.
       Documentation at /docs/config/service.html
   -->
  <Service name="Catalina">

    <!--The connectors can use a shared executor, you can define one or more named thread pools-->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->


    <!-- A "Connector" represents an endpoint by which requests are received
         and responses are returned. Documentation at :
         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
         Java AJP  Connector: /docs/config/ajp.html
         APR (HTTP/AJP) Connector: /docs/apr.html
         Define a non-SSL HTTP/1.1 Connector on port 8080
    -->
    <Connector connectionTimeout="20000" port="8888" protocol="HTTP/1.1" redirectPort="8445"/>
    <!-- A "Connector" using the shared thread pool-->
    <!--
    <Connector executor="tomcatThreadPool"
               port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    -->
    <!-- Define a SSL HTTP/1.1 Connector on port 8443
         This connector uses the BIO implementation that requires the JSSE
         style configuration. When using the APR/native implementation, the
         OpenSSL style configuration is required as described in the APR/native
         documentation -->
    <!--
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" />
    -->

    <!-- Define an AJP 1.3 Connector on port 8009 -->
    <Connector port="8019" protocol="AJP/1.3" redirectPort="8443"/>


    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine defaultHost="localhost" name="Catalina">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

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

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>

            <Context docBase="/var/tomcat-test/webapps/TestTomcat" path="/TestTomcat" reloadable="true" source="org.eclipse.jst.jee.server:TestTomcat"/>
        </Host>
    </Engine>
  </Service>
</Server>
View Code

在新建server的時候會解析server.xml,同時也會根據我們上面的配置新建StandardContext,而且會將StandardContext作為Host的一個Child(可以在Catalina.createStartDigester方法中查看怎麼解析server.xml),在tomcat的啟動過程中context是由父容器host啟動的

額,看到這張圖發現似曾相識,對滴,就是Context的初始化,因為在tomcat中一個context實例就代表一個webapp,所以其實應用程式webapp的發佈本身就是context的初始化和啟動,再看看context的啟動

以上已經完整展示了一個webapp的發佈過程,也就是在server.xml的配置的webapp發佈過程,接著來看看在webapps目錄下的發佈過程。

在webapps目錄下

在StandardHost的startInternal方法中調用了父類的ContainerBase.startInternal方法,在StandardHost發佈完在server.xml中配置的app之後,會調用setState來切換自身的狀態,這個時候就會觸發listener HostConfig的lifecycleEvent方法

@Override
protected synchronized void startInternal() throws LifecycleException {

    // Start our subordinate components, if any
    if ((loader != null) && (loader instanceof Lifecycle))
        ((Lifecycle) loader).start();
    logger = null;
    getLogger();
    if ((manager != null) && (manager instanceof Lifecycle))
        ((Lifecycle) manager).start();
    if ((cluster != null) && (cluster instanceof Lifecycle))
        ((Lifecycle) cluster).start();
    Realm realm = getRealmInternal();
    if ((realm != null) && (realm instanceof Lifecycle))
        ((Lifecycle) realm).start();
    if ((resources != null) && (resources instanceof Lifecycle))
        ((Lifecycle) resources).start();

    // Start our child containers, if any
   // 因為在解析server.xml的時候已經新建了Context,這裡就可以直接start Container children[] = findChildren(); List<Future<Void>> results = new ArrayList<Future<Void>>(); for (int i = 0; i < children.length; i++) { results.add(startStopExecutor.submit(new StartChild(children[i]))); } boolean fail = false; for (Future<Void> result : results) { try { result.get(); } catch (Exception e) { log.error(sm.getString("containerBase.threadedStartFailed"), e); fail = true; } } if (fail) { throw new LifecycleException( sm.getString("containerBase.threadedStartFailed")); } // Start the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start();    // 調用lifecycleEvent方法,觸發StandardHost的start事件,觸發監聽器HostConfig的start方法,所有在webapps下麵的都在start方法中發佈 setState(LifecycleState.STARTING); // Start our thread threadStart(); }

調用過程如下:

(上圖中主要畫出了deployWars的調用過程,其他兩個deploy方法也類似)

在HostConfig.deployApps方法中主要進行了:

  • 調用deployDescriptors:發佈所有使用xml配置的webapp,因為可能有多個xml,所以在該方法內部又調用了deployDescriptor來發佈每個xml對應的webapp
  • 調用deployWars:發佈所有在webapps目錄下的war包,也可能存在多個
  • 調用deployDirectories:發佈所有直接部署在webapps目錄下的應用程式

前面說過了,發佈webapp就是新建一個context對象並初始化、啟動,上面三個方法中主要的作用為:

Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
    (LifecycleListener) clazz.newInstance();
context.addLifecycleListener(listener);

context.setName(cn.getName());
context.setPath(cn.getPath());
context.setWebappVersion(cn.getVersion());
context.setDocBase(cn.getBaseName() + ".war");
host.addChild(context);
  • 構建一個StandardContext
  • 將context添加到host中,在Container.addChildInternal方法中會調用context.start

接下來的步驟就和上面在server.xml配置的webapp啟動一致了。

reload

在我們使用tomcat開發web的時候經常會用到“熱載入”(熱部署)功能,那麼原理究竟是什麼呢?上面介紹了webapp發佈,因為reload功能也是從HostConfig開始的,所以繼續介紹reload功能

上面的ContainerBase.startInternal是由StandardEngine.startInternal方法調用的,啟動了一個daemon線程定時檢測webapp是否發生變化(文件是否被修改),如果被修改了則會重新啟動StandardContext。

總結

像tomcat部署和發佈,我們天天都在用,可是不知道究竟原理怎麼樣的,在學習了tomcat源碼之後,對這一切都更加明瞭。知其然,知其所以然。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • python操作Mysql,很方便,使用的MySQLdb的庫,基本的操作如下: 查詢: 插入數據: 使用過程中遇到了編碼的問題,使用utf-8解決編碼問題: 還有遇到反斜杠的問題,mysql預設把反斜杠轉義了,我的解決方法是將反斜杠換成雙反斜杠: mysql語句需要格式化字元串,查詢的sql字元串需 ...
  • 前言:JSTL(JSP Standard Tag Library)JSP標準標簽庫。它的目的是為了簡化JSP的開發,如何沒有JSTL可能我們開發的時候就需要寫大量的自定義標簽,無疑會加大開發難度,有了JSTL以後我們就不需要寫那些普通的標簽(除非特殊的)這樣一來我們的開發效率也會提升,在這裡我主要講 ...
  • 參考了幾篇文章,和錯誤查詢,最後總結如下 一、下載 我比較傾向於使用mercurial來獲取源代碼,雖然你得挑網路穩定的時候更新,但是易更新。 從官網查找一下,可以通過以下步驟完成源代碼的下載 1. 其中java的版本可以上 http://hg.openjdk.java.net/ 自由選擇,路徑正確 ...
  • 一、page指令 page指令是最常用的指令,用來說明JSP頁面的屬性等。JSP指令的多個屬性可以寫在一個page指令里,也可以寫在多個指令里。但需要註意的是,無論在哪個page指令里的屬性,任何page允許的屬性都只能出現一次,否則會出現編譯錯誤。import屬性除外,可以出現多次。屬性名稱區分大 ...
  • 初學java EE,雖然知道使用框架會使開發更加便捷高效,但是對於初學者來說,感到使用框架比較迷惑,尤其是各種jar包的引用、各種框架的配置、註解的使用等等。 最好的學習方法就是實踐,於是下載了一個現成的DEMO,通過簡單的修改先成功在自己電腦上跑起來,然後再逐個文件進行分析學習,最終才能從總體的高 ...
  • SSM框架的Web程式主要用到了三個技術: 要完成一個功能: 簡單點就是: DataBase > Entity > Mapper.xml > Mapper.java > Service.java > Controller.java > Jsp. ...
  • 一共 15 篇隨筆,主要是為了記錄數據分析過程中的一些小 demo,分享給其他需要的網友,更為了方便以後自己查看,15 篇隨筆,每篇內容基本都是以一句說明加一段代碼的方式, 保持簡單小巧,看起來也清晰 ,一共可以劃分為三個大部分: 第一部分簡單介紹數據分析,以一個小例子簡單說明瞭什麼是數據分析和 I ...
  • --> List 列表中的自動添加的多餘空間長度該怎麼去除呢?... --> 還是沒有解決多餘空間的問題啊... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...