Tomcat

1. Tomcat 整體結構

圖 1.Tomcat 的整體結構

從上圖中能夠看出 Tomcat 的心臟是兩個組件:Connector 和 Container。Connector 組件是能夠被替換,這樣能夠提供給服務器設計者更多的選擇,由於這個組件是如此重要,不只跟服務器的設計的自己,並且和不一樣的應用場景也十分相關,因此一個 Container 能夠選擇對應多個 Connector。多個 Connector 和一個 Container 就造成了一個 Service,Service 的概念你們都很熟悉了,有了 Service 就能夠對外提供服務了,可是 Service 還要一個生存的環境,必需要有人可以給她生命、掌握其生死大權,那就非 Server 莫屬了。因此整個 Tomcat 的生命週期由 Server 控制。目前我們公司用的tomcat的版本Server version:Apache Tomcat/7.0.52.html

2. Tomcat相關接口

2.1 Tomcat接口視圖

Catalina:與開始/關閉shell腳本交互的主類,所以若是要研究啓動和關閉的過程,就從這個類開始看起。java

Server:是整個Tomcat組件的容器,包含一個或多個Service。web

Service:Service是包含Connector和Container的集合,Service用適當的Connector接收用戶的請求,再發給相應的Container來處理。spring

Connector:實現某一協議的鏈接器,如默認的有實現HTTP、HTTPS、AJP協議的。sql

Container:能夠理解爲處理某類型請求的容器,處理的方式通常爲把處理請求的處理器包裝爲Valve對象,並按必定順序放入類型爲Pipeline的管道里。Container有多種子類型:Engine、Host、Context和Wrapper,這幾種子類型Container依次包含,處理不一樣粒度的請求。另外Container裏包含一些基礎服務,如Loader、Manager和Realm。shell

Engine:Engine包含Host和Context,接到請求後仍給相應的Host在相應的Context裏處理。數據庫

Host:就是咱們所理解的虛擬主機。apache

Context:就是咱們所部屬的具體Web應用的上下文,每一個請求都在是相應的上下文裏處理的。api

Wrapper:Wrapper是針對每一個Servlet的Container,每一個Servlet都有相應的Wrapper來管理。瀏覽器

能夠看出Server、Service、Connector、Container、Engine、Host、Context和Wrapper這些核心組件的做用範圍是逐層遞減,並逐層包含。

下面就是些被Container所用的基礎組件:

Loader:是被Container用來載入各類所需的Class。

Manager:是被Container用來管理Session池。

Realm:是用來處理安全裏受權與認證。

2.2 Service 和 Server接口重要方法

 

Service接口
 1 public abstract interface Service extends Lifecycle
 2 {
 3 public abstract Container getContainer();
 4 public abstract void setContainer(Container paramContainer);
 5 public abstract String getInfo();
 6 public abstract String getName();
 7 public abstract void setName(String paramString);
 8 public abstract Server getServer();
 9 public abstract void setServer(Server paramServer);
10 public abstract ClassLoader getParentClassLoader();
11 public abstract void setParentClassLoader(ClassLoader paramClassLoader);
12 public abstract void addConnector(Connector paramConnector);
13 public abstract Connector[] findConnectors();
14 public abstract void removeConnector(Connector paramConnector);
15 public abstract void addExecutor(Executor paramExecutor);
16 public abstract Executor[] findExecutors();
17 public abstract Executor getExecutor(String paramString);
18 public abstract void removeExecutor(Executor paramExecutor);
19 }

 

 

從 Service 接口中定義的方法中能夠看出,它主要是爲了關聯 Connector 和 Container,同時會初始化它下面的其它組件,注意接口中它並無規定必定要控制它下面的組件的生命週期。全部組件的生命週期在一個 Lifecycle 的接口中控制。Tomcat 中 Service 接口的標準實現類是 StandardService 它不只實現了 Service 藉口同時還實現了 Lifecycle 接口,這樣它就能夠控制它下面的組件的生命週期了。 StandardService 中主要的幾個方法實現的代碼,下面是 setContainer 和 addConnector 方法的源碼:

StandardService.setContainer
 1 public void setContainer(Container container) {
 2       Container oldContainer = this.container;
 3       if ((oldContainer != null) && (oldContainer instanceof Engine))   //判斷當前的這個 Service 有沒有已經關聯了 Container,若是已經關聯了,那麼去掉這個關聯關係
 4           ((Engine) oldContainer).setService(null);
 5       this.container = container;
 6       if ((this.container != null) && (this.container instanceof Engine))
 7           ((Engine) this.container).setService(this);
 8       if (getState().isAvailable() && (this.container != null)) {
 9           try {
10               this.container.start();
11           } catch (LifecycleException e) {
12               // Ignore
13           }
14       }
15       if (getState().isAvailable() && (oldContainer != null)) {
16           try {
17               oldContainer.stop();          //若是這個 oldContainer 已經被啓動了,結束它的生命週期
18           } catch (LifecycleException e) {
19               // Ignore
20           }
21       }
22       // Report this property change to interested listeners
23       support.firePropertyChange("container", oldContainer, this.container);
24   }

 

StandardService.addConnector
 1 public void addConnector(Connector connector) {
 2        synchronized (connectorsLock) {
 3            connector.setService(this);
 4            Connector results[] = new Connector[connectors.length + 1];
 5            System.arraycopy(connectors, 0, results, 0, connectors.length);
 6            results[connectors.length] = connector;
 7            connectors = results;
 8            if (getState().isAvailable()) {
 9                try {
10                    connector.start();
11                } catch (LifecycleException e) {
12                    log.error(sm.getString(
13                            "standardService.connector.startFailed",
14                            connector), e);
15                }
16            }
17            // Report this property change to interested listeners
18            support.firePropertyChange("connector", null, connector);
19        }
20    }

 

 

Server類是提供一個接口讓其餘程序可以訪問到這個service集合,同時要維護它所包含的全部的Service的生命週期,包括如何初始化,如何結束服務,如何找到別人要訪問的Service。它的標準實現類 StandardServer 實現server這些方法,同時也實現了LifecycleMBeanBase接口的全部方法,LifecycleMBeanBase接口實現了MBeanRegistration並繼承了LifecycleBase接口。LifecycleBase接口實現了Lifecycle接口。因此Lifecycle接口的方法實現都在其它組件中,因此組件的生命週期由包含它的父組件控制。如Server的Start方法和Stop方法就會調用Service組件的相關方法。下面主要看一下 StandardServer 重要的一個方法 addService 的實現:

StandardServer.addService
 1 @Override
 2   public void addService(Service service) {
 3       service.setServer(this);  //關聯Server和Service
 4       synchronized (servicesLock) {
 5           Service results[] = new Service[services.length + 1];
 6           System.arraycopy(services, 0, results, 0, services.length);
 7           results[services.length] = service;
 8           services = results;
 9           if (getState().isAvailable()) {
10               try {
11                   service.start();
12               } catch (LifecycleException e) {
13                   // Ignore
14               }
15           }
16           // Report this property change to interested listeners
17           support.firePropertyChange("service", null, service);
18       }
19   }

 

 

3. Tomcat重要配置文件Server.xml

3.1 server.xml

 Tomcat服務器是由一系列可配置的組件構成,其中核心的組件是Catalina Servlet容器,它是全部其餘Tomcat組件的頂層容器。Tomcat的組件在<CATALINA_HOME>/conf/server.xml文件中進行配置,每一個Tomcat組件在server.xml文件中對應一種配置元素。一下代碼以XML的形式展現各類Tomcat組件之間的關係(如下是fat環境的tomcat的server.xml配置):

Server.xml
 1 <?xml version='1.0' encoding='utf-8'?>
 2 <Server port="8005" shutdown="SHUTDOWN">
 3   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
 4   <Listener className="org.apache.catalina.core.JasperListener" />
 5   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
 6   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
 7   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
 8   <GlobalNamingResources>
 9     <Resource name="UserDatabase" auth="Container"
10               type="org.apache.catalina.UserDatabase"
11               description="User database that can be updated and saved"
12               factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
13               pathname="conf/tomcat-users.xml" />
14   </GlobalNamingResources>
15   <Service name="Catalina">
16     <Connector port="8080" protocol="HTTP/1.1"
17                 socketBuffer="9000"
18                 enableLookups="false"
19                 tcpNoDelay="true"
20                 minSpareThreads="20"
21                 maxThreads="1024"
22                 connectionTimeout="5000"
23                 maxHttpHeaderSize="32768"
24                 acceptCount="150"
25                 redirectPort="8443" />
26     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
27     <Engine name="Catalina" defaultHost="localhost">
28       <Realm className="org.apache.catalina.realm.LockOutRealm">
29         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
30                resourceName="UserDatabase"/>
31       </Realm>
32       <Host name="localhost"  appBase="/opt/ctrip/web/work"
33             unpackWARs="true" autoDeploy="true">
34       
35          <Context path="/btbjob" crossContext="true" reloadable="true" docBase="/opt/ctrip/web/jobws.btborder.flight.charter-jobws-btborder/work"/>
36         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/opt/logs/tomcat"
37                prefix="access." suffix=".log"
38                pattern="%h %l %u %t &quot;%r&quot; %s %b %T" />
39       </Host>
40     </Engine>
41   </Service>
42 </Server>

 

 

3.2 Server.xml元素詳解

元素名

 

 

屬性

 

 

解釋

 

 

server

port

指定一個端口,這個端口負責監聽關閉tomcat的請求

shutdown

指定向端口發送的命令字符串

service

name

指定service的名字

Connector(表示客戶端和service之間的鏈接)

port

指定服務器端要建立的端口號,並在這個斷口監聽來自客戶端的請求

protocol

瀏覽器請求必須爲HTTP1.1

若是使用AJP處理器,該值必須爲AJP/1.3

socketBuffer

設Socket輸出緩衝區的大小(以字節爲單位),-1表示禁止緩衝,默認值爲9000字節

tcpNoDelay

爲true時,能夠提升性能。默認值爲true

minSpareThreads

設當鏈接器第一次啓協建立線程的數目,確保至少有這麼多的空閒線程可用。默認值爲4

maxHttpHeaderSize

HTTP請求和響應頭的最大量,以字節爲單位,默認值爲4096字節

maxSpareThreads

容許存在空閒線程的最大數目,默認值爲50

enableLookups

若是爲true,則能夠經過調用request.getRemoteHost()進行DNS查詢來獲得遠程客戶端的實際主機名,若爲false則不進行DNS查詢,而是返回其ip地址

redirectPort

指定服務器正在處理http請求時收到了一個SSL傳輸請求後重定向的端口號

acceptCount

指定當全部可使用的處理請求的線程數都被使用時,能夠放處處理隊列中的請求數,超過這個數的請求將不予處理

connectionTimeout

指定超時的時間數(以毫秒爲單位)

Engine(表示指定service中的請求處理機,接收和處理來自Connector的請求)

defaultHost

指定缺省的處理請求的主機名,它至少與其中的一個host元素的name屬性值是同樣的

Context(表示一個web應用程序,一般爲WAR文件,關於WAR的具體信息見servlet規範)

docBase

應用程序的路徑或者是WAR文件存放的路徑

path

表示此web應用程序的url的前綴,這樣請求的url爲http://localhost:8080/path/****

reloadable

這個屬性很是重要,若是爲true,則tomcat會自動檢測應用程序的/WEB-INF/lib 和/WEB-INF/classes目錄的變化,自動裝載新的應用程序,咱們能夠在不重起tomcat的狀況下改變應用程序

host(表示一個虛擬主機)

name

指定主機名

appBase

應用程序基本目錄,即存放應用程序的目錄

autoDeploy

指示Tomcat運行時,若有新的WEB程序加開appBase指定的目錄下,是否爲自動佈署,默認值爲true

unpackWARs

若是爲true,則tomcat會自動將WAR文件解壓,不然不解壓,直接從WAR文件中運行應用程序

Logger(表示日誌,調試和錯誤信息)

className

指定logger使用的類名,此類必須實現org.apache.catalina.Logger 接口

prefix

指定log文件的前綴

suffix

指定log文件的後綴

timestamp

若是爲true,則log文件名中要加入時間,以下例:localhost_log.001-10-04.txt

Realm(表示存放用戶名,密碼及role的數據庫)

className

指定Realm使用的類名,此類必須實現org.apache.catalina.Realm接口

Valve(功能與Logger差很少,其prefix和suffix屬性解釋和Logger 中的同樣)

className

指定Valve使用的類名,如用org.apache.catalina.valves.AccessLogValve類能夠記錄應用程序的訪問信息

directory

指定log文件存放的位置

pattern

有兩個值,common方式記錄遠程主機名或ip地址,用戶名,日期,第一行請求的字符串,HTTP響應代碼,發送的字節數。combined方式比common方式記錄的值更多

3.4 配置HTTPS鏈接器

 

4. Tomcat部署方式

4.1 Tomcat部署

tomcat部署方式有三種:

  • 在tomcat中的conf目錄,修改server.xml的<host/>節點添加<Context />配置.

    <Context path="/hello" docBase="D:eclipse3.2.2forwebtoolsworkspacehelloWebRoot" debug="0" privileged="true"> 
    </Context>

  • 將web項目文件拷貝到webapps目錄下.
  • 在conf目錄中,新建Catalina\localhost目錄,在該目錄建立一個xml文件,名字隨意,只要和當前文件不重名,該xml文件的內容爲:

    <Context path="/hello" docBase="D:eclipse3.2.2forwebtoolsworkspacehelloWebRoot" debug="0" privileged="true"> 
    </Context>
    注:一個Tomcat若要部署多個項目,在Server.xml文件添加<Context/>如:

     <Context path="/btbjob_one" crossContext="true" reloadable="true" docBase="/opt/ctrip/web/jobws.btborder.flight.charter-jobws-btborder/work"/>
     <Context path="/btbjob_two" crossContext="true" reloadable="true" docBase="/opt/ctrip/web/jobws.btborder.flight.charter-jobws-btborder/work"/>

4.2 Tomcat熱部署

     熱部署是指在你對項目代碼(不管是JSP、JAVA類,甚至是配置文件)進行了修改時,在不重啓WEB服務器前提下能讓修改生效。

  1.  修改Tomcat安裝目錄下的conf下的context.xml,在<Context>節點上添加reloadable="true"屬性
  2.  

  3. 進入myEclipse - > preference -> Tomcat 7.x -> JDK , 在Optional Java VM arguments中填入 -Dcom.sun.management.jmxremote=true 以下圖:

     

5. Tomcat 處理Http請求流程

Tomcat Server處理請求的過程,以下以HTTP請求爲例:

由上圖可見,Request的解析和加工過程不是在一個方法裏搞定,而是信息流動過程當中逐步解析的,不一樣層次的處理器解析不一樣層次的信息,在解析過程同時作了些判斷和攔截的工做,好比當發現是要訪問WEB-INF的資源,會直接返回錯誤給客戶端等等。

6. Tomcat開發中常見問題分享

6.1 Tomcat常見錯誤

tomcat啓動時錯誤

1 The JAVA_HOME environment variable is not defined This environment variable is needed to run this program;

Re:沒有在tomcat的配置文件.bash_profile中設置環境變量JAVA_HOME,具體設置方法爲:加入以下幾行:

   JAVA_HOME=/home/tomcat/j2sdk1.4.2_08(具體值要以實際的jdk安裝路徑爲準)

   export JAVA_HOMECLASSPATH=/home/tomcat/j2sdk1.4.2_08/lib/tools.jar:/home/tomcat/j2sdk1.4.2_08/lib/dt.jar

   export CLASSPATH

2 Error occurred during initialization of VM Could not reserve enough space for object heap

Re:在tomcat的bin目錄下,catalina.sh文件的tomcat內存參數配置過大,超過機器可用內存總數形成,修改到適當的值便可,修改的參數爲:JAVA_OPTS="-Xms50m -Xmx60m"

3 tomcat啓動時報某個目錄沒有權限,啓動失敗,或者不能執行某些jsp頁

Re:tomcat須要tomcat用戶具備一些目錄和文件的相應權限, 全部目錄應該具備讀寫執行(瀏覽)的權限,jsp,class文件應該最少具備讀權限, 一些文件須要寫權限,下面是已知的須要讀寫權限文件的列表:

       $CATALINA_HOME/logs下全部文件

       $CATALINA_HOME/work下全部文件

       $CATALINA_HOME/publish/main/count.txt文件

       $CATALINA_HOME/publish/chatroom/resource下的全部.xml文件

       全部上傳圖片目錄都須要寫權限。

       改變文件目錄權限的方法:執行下面命令,設置全部的tomcat安裝下的文件和目錄,能夠保證執行,可是不是很安全。

1. 設置tomcat上級目錄/opt全部用戶都有讀寫執行權限:

chmod 777 [tomcat的上級目錄]

2. 設置tomcat目錄下的全部文件的屬主爲deploy:

      chown -R tomcat [tomcat安裝目錄]

3. 設置全部tomcat下的全部文件和目錄的屬主(deploy)具備讀寫執行權限

chmod 700 -R /opt/tomcat

注意:公司測試環境tomcat所屬主爲deploy,切勿使用root主去啓動tomcat.

4  執行startup.sh文件後告訴地址已經使用,致使tomcat不能啓動

Re:多是前一次執行./shutdown.sh文件關閉tomcat時沒有中止已經啓動的Java進程,而這個進程仍然在監聽tomcat所使用的端口,或者有另一個tomcat正在運行,server.xml文件中的監聽端口和當前tomcat衝突。

   使用下面命令查看當前系統是否有正在監聽的端口(8080--webcache, 90--www或http).

1. 列出全部系統正在監聽的端口和綁定的地址netstat -l

2. 找出當前系統中的全部進程,管道符號過濾輸出顯示包含java字符串的行

     ps -A |grep java       

3. 結束一個指定的進程:

     kill   [進程號]

4. 強制殺死一個進程:

      kill -9 [進程號]

     若是肯定不是別的tomcat運行致使的衝突或者沒有java進程運行能夠再次運行startup.sh文件啓動tomcat

 tomcat運行時錯誤

1. org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Network error IOException: Connection refused: connect)
2. Caused by: java.sql.SQLException: Network error IOException: Connection refused: connect
3. Caused by: java.net.ConnectException: Connection refused: connect

Re:數據庫未開狀況下運行tomcat出現的問題,把數據庫打開就好了

4.java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V
at net.sf.cglib.core.ClassEmitter.begin_class(ClassEmitter.java:77) 這個問題是最最多見的,第一次整合ssh的時候會發現這個問題,有時候刪除掉相關的包仍是會衝突。因此解決辦法我通常是:

Re:把MyEclipse中的hibernate中的有關ASM的包所有刪除,將spring中asm包拷貝進去重啓就Ok了,有時候須要刪除其餘文件,到網上找找吧;

還有個解決辦法:Spring 和 Hibernate 共用的一些 jar 文件發生了版本衝突, 刪除 WEB-INF/lib/asm-2.2.3.jar 而後重啓 Tomcat.


5 .javax.servlet.jsp.JspException: Invalid argument looking up property usersVO.account of bean loginForm

Re,通常提示這種錯誤表示VO中的form沒有實例化,在reset方法中new一下就Ok了。養成良好的編碼習慣能夠避免這種簡單錯誤。

6.嚴重: Exception loading sessions from persistent storage

Re:tomcat安裝目錄\work\Catalina\localhost\{webAppName}\SESSIONS.ser,刪除此文件

6.2 Tomcat 常見的錯誤碼

 出現404錯誤是由於你所請求的頁面不可用!這是response對象的http響應中的狀態行404表示當前請求的頁面不可用!200表示測試經過。500表示服務器內部發生錯誤等!

具體錯誤碼對用的信息參見:http://blog.csdn.net/lidawei201/article/details/8482006

6.3 Tomcat 開發中遇中文亂碼問題

tomcat8以前的默認編碼是ISO-8859-1,tomcat8的默認編碼是UTF-8.

解決中文亂碼的問題:若是請求方式GET.能夠更改${CATALINA_HOME}/conf/server.xml中對應的Connector中指定URIEncoding="UTF-8"參數.

                                   若是請求方式POST.能夠經過Filter來設置編碼.Tomcat7.x (已經將這個Filter加入Tomcat內置了,直接複製一下代碼到你的項目web.xml中)

 

 1 <filter>
 2 <filter-name>setCharacterEncodingFilter</filter-name>
 3 <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
 4 <init-param>
 5   <param-name>encoding</param-name>
 6   <param-value>UTF-8</param-value>
 7 </init-param>
 8 </filter>
 9 <filter-mapping>
10 <filter-name>setCharacterEncodingFilter</filter-name>
11 <url-pattern>/*</url-pattern>
12 </filter-mapping>

 


若是這樣設置仍是有亂碼問題,須要考慮下數據庫的編碼.