Tomcat系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html
1. 入門示例:虛擬主機提供web服務
該示例通過設置虛擬主機來提供web服務,因為是入門示例,所以設置極其簡單,只需修改$CATALINA_HOME/conf/server.xml文件為如下內容即可,本文的tomcat安裝在/usr/local/tomcat下,因此$CATALINA_HOME=/usr/local/tomcat
。其中大部分都采用了默認設置,只是在engine容器中添加了兩個Host容器。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container" type="org.apache.catalina.UserDatabase" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" enableLookups="false" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" />
</Realm>
<!-- 從此處開始添加以下兩個Host容器作為虛擬主機 -->
<!-- 定義一個在$CATALINA_HOME之外的虛擬主機 -->
<Host name="www.longshuai.com" appBase="/www/longshuai" unpackWARs="true" autoDeploy="true">
<Context path="" docBase="/www/longshuai" reloadable="true" />
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="longshuai_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" />
</Host>
<!-- 定義一個在$CATALINA_HOME/webapps下的虛擬主機 -->
<Host name="www.xiaofang.com" appBase="webapps/xiaofang" unpackWARs="true" autoDeploy="true">
<Context path="" docBase="" reloadable="true" />
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="xiaofang_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" />
</Host>
<!-- 默認虛擬主機localhost,可不修改 -->
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
除了engine中定義的默認localhost虛擬主機,另外布置了兩個虛擬主機www.longshuai.com和www.xiaofang.com,它們的程序目錄分別為/www/longshuai和$CATALINA_HOME/webapps/xiaofang,所以需要提前建立好這兩個目錄。另外,在context中定義了docBase,對於uri路徑/xuexi,它的文件系統路徑分別為/www/longshuai/xuexi目錄和$CATALINA_HOME/webapps/xiaofang/xuexi,所以也要在上面兩個程序目錄中定義好xuexi目錄。除此之外,還分別為這3個虛擬主機定義了日志,它們的路徑為相對路徑logs,相對於$CATALINA_HOME。
再提供appBase目錄和docBase目錄。
mkdir -p /www/longshuai/xuexi
mkdir -p /usr/local/tomcat/webapps/xiaofang/xuexi
再提供測試用的index.jsp文件。內容大致如下,分別復制到以下四個目錄中:
/www/longshuai/
/www/longshuai/xuexi/
/usr/local/tomcat/webapps/xiaofang/
/usr/local/tomcat/webapps/xiaofang/xuexi
並將out.println的輸出內容分別稍作修改,使能夠區分讀取的是哪個index.jsp。
<%@ page language="java" %>
<%@ page import="java.util.*" %>
<html>
<body>
<% out.println("hello world from longshuai Root"); %>
</body>
</html>
最后重啟catalina。
catalina.sh stop catalina.sh start
再測試主機上添加www.{longshuai,xiaofang}.com的host記錄。例如在windows上,在C:\Windows\System32\drivers\etc\hosts中添加如下記錄:
192.168.100.22 www.longshuai.com www.xiaofang.com
在瀏覽器中進行測試,結果如下:
2. tomcat體系結構基本說明
如下兩圖:上面的圖是tomcat組件體系的簡圖,下面的圖是Service組件細化后的圖。
tomcat高度模塊化,各個模塊之間有嵌套的父子關系。如果使用配置文件來描述,可以大致簡化為如下:
<server>
<service>
<connector PORT />
<engine>
<host name=www.a.com appBase=/www/a >
<context path="" docBase=/www/a />
<context path="/xuexi" docBase=/www/a/xuexi />
</host>
<host>
<context />
</host>
</engine>
</service>
</server>
其中:
server
組件是管理tomcat實例的組件,可以監聽一個端口,從此端口上可以遠程向該實例發送shutdown關閉命令。service
組件是一個邏輯組件,用於綁定connector和container,有了service表示可以向外提供服務,就像是一般的daemon類服務的service。可以認為一個service就啟動一個JVM,更嚴格地說,一個engine組件才對應一個JVM(定義負載均衡時,jvmRoute就定義在Engine組件上用來標識這個JVM),只不過connector也工作在JVM中。connector
組件是監聽組件,它有四個作用:- (1).開啟監聽套接字,監聽外界請求,並和客戶端建立TCP連接;
- (2).使用protocolHandler解析請求中的協議和端口等信息,如http協議、AJP協議;
- (3).根據解析到的信息,使用processer將分析后的請求轉發給綁定的Engine;
- (4).接收響應數據並返回給客戶端。
container
是容器,它是一類組件,在配置文件(如server.xml)中沒有體現出來。它包含4個容器類組件:engine容器、host容器、context容器和wrapper容器。engine
容器用於從connector組件處接收已建立的TCP連接,還用於接收客戶端發送的http請求並分析請求,然后按照分析的結果將相關參數傳遞給匹配出的虛擬主機。engine還用於指定默認的虛擬主機。host
容器定義虛擬主機,由於tomcat主要是作為servlet容器的,所以為每個webapp指定了它們的根目錄appBase。context
容器主要是根據path和docBase獲取一些信息,將結果交給其內的wrapper組件進行處理(它提供wrapper運行的環境,所以它叫上下文context)。一般來說,都采用默認的標准wrapper類,因此在context容器中幾乎不會出現wrapper組件。wrapper
容器對應servlet的處理過程。它開啟servlet的生命周期,根據context給出的信息以及解析web.xml中的映射關系,負責裝載相關的類,初始化servlet對象init()、執行servlet代碼service()以及服務結束時servlet對象的銷毀destory()。executor
組件為每個Service組件提供線程池,使得各個connector和Engine可以從線程池中獲取線程處理請求,從而實現tomcat的並發處理能力。一定要注意,Executor的線程池大小是為Engine組件設置,而不是為Connector設置的,Connector的線程數量由Connector組件的acceptorThreadCount屬性來設置。如果要在配置文件中設置該組件,則必須設置在Connector組件的前面,以便在Connector組件中使用`executor`屬性來引用配置好的Executor組件。如果不顯式設置,則采用Connector組件上的默認配置,默認配置如下:- (1).maxThreads:最大線程數,默認值200。
- (2).minSpareThreads:最小空閑線程數,默認值25。
- (3).maxIdleTime:空閑線程的線程空閑多長時間才會銷毀,默認值60000即1分鍾。
- (4).prestartminSpareThreads:是否啟動executor時就直接創建等於最小空閑線程數的線程,默認值為false,即只在有連接請求進入時才會創建。
根據上面描述的tomcat組件體系結構,處理請求的大致過程其實很容易推導出來:
Client(request)-->Connector-->Engine-->Host-->Context-->Wrapper(response data)-->Connector(response header)-->Client
撇開tomcat作為servlet容器的行為。它和apache、nginx的功能大致都能對應上。例如以nginx為例,以下是nginx提供web服務時的配置結構:
server {
listen PORT;
server_name www.a.com; # 對應於<host name=www.a.com>
location / { # 對應於context path=""
root html; # 對應於docBase
}
location /xuexi { # 對應於context path="/xuexi"
root html/xuexi;
}
}
connetcor組件類似於nginx的listen指令。host容器類似於nginx的server指令,host容器中的name屬性相當於nginx的server_name指令。engine組件則沒有對應配置項,不過在nginx同樣有engine的功能,例如默認的虛擬主機,分析URL來判斷請求交給哪個虛擬主機處理等。context容器相當於location指令,context容器的path屬性相當於location的uri匹配路徑,docBase相當於location的中的root指令,即DocumentRoot。
tomcat作為簡單的web服務程序大致如此,但它的核心畢竟是處理servlet和jsp,它必須得管理好每個webapp。因此,對於tomcat來說,必須要掌握部署webapp的方式。在tomcat上部署webapp時,必須要理解context的概念。對於tomcat而言,每個context都應該算是一個webapp,其路徑由docBase決定,該目錄存放的是歸檔的war文件或未歸檔的webapp相關文件,而host容器中的appBase則是虛擬主機整理webapp的地方,一個appBase下可以有多個webapp,即多個context。
3. tomcat的appBase和docBase詳細說明
這兩貨雖然意義很明確,但"潛規則"很嚴重。以下面的配置為例。
<host name=www.a.com appBase=/www/a >
<context path="" docBase=/www/a />
<context path="/xuexi" docBase=/www/a/xuexi />
</host>
appBase是虛擬主機存放webapp的目錄,它可以是相對路徑,也可以是絕對路徑。如果是相對路徑,則相對於$CATALINA_HOME,嚴格並准確地說是$CATALINA_BASE。
path是URI的匹配路徑,相當於nginx的location后的路徑。tomcat要求每個虛擬主機必須配置一個空字符串的path,該條context作為URI無法被明確匹配時的默認context,它相當於nginx中location / {}
的作用。
docBase則是每個webapp的存放目錄(或者是已歸檔的war文件),它可以是相對路徑,也可以是絕對路徑,提供相對路徑時它相對於appBase。該目錄一般在appBase的目錄下,但並不規定一定要放在appBase下。對於web服務來說,它相當於nginx的root指令,但對於webapp來說,一個context就相當於一個webapp,而docBase正是webapp的路徑。
"潛規則"在於默認的context如何提供。有以下幾種情況:
- 明確定義了
<context path="" docBase=webappPATH>
,此時默認context的處理路徑為webappPATH。 - 明確定義了
<context path="">
,但卻沒給定docBase屬性,此時該默認context處理路徑為appBase/ROOT目錄,注意ROOT為大寫。 - 完全沒有定義
path=""
的context時,即host容器中沒有明確的path="",此時將隱式定義一個默認context,處理路徑為appBase/ROOT目錄。 - 定義了path但沒有定義docBase屬性時,docBase將根據path推斷出它的路徑。推斷的規則如下:(注:此時推斷的不是默認context,而是對應context的docbase)
context path context name 推斷出的docBase路徑
--------------------------------------------------
/foo /foo foo
/foo/bar /foo/bar foo/bar
Empty String Empty String ROOT
顯然,沒有給定path=""或缺少docbase時,都以ROOT作為目錄。以下是幾個定義示例:
# 虛擬主機中沒有定義任何context,將以appBase下的ROOT作為默認處理路徑
<Host appBase="webapps">
</Host>
# 沒有定義path=""的context,但定義了path非空的context,也將以ROOT作為默認處理路徑
# 如果下面的Context容器中省略docBase屬性,則推斷出該context的docBase路徑為appBase/xuexi
<Host appBase="webapps">
<Context path="/xuexi" docBase="webappPATH" />
</Host>
# 某個context定義了path="",該context將作為默認context
# 但該默認context如果沒有定義docBase,將推斷出其docBase路徑為appBase/ROOT
<Host appBase="webapps">
<Context path="" docBase="webappPATH" />
</Host>
# 某個context定義了path="",該context將作為默認context
# 下面的默認context明確定義了docBase
<Host appBase="webapps">
<Context path="" docBase="webappPATH" />
</Host>
舉個直觀的例子,如果某個Host配置如下。
<Host name="www.xiaofang.com" appBase="/www/xiaofang" unpackWARs="true" autoDeploy="true">
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
</Host>
那么瀏覽器訪問http://www.xiaofang.com:8080/xuexi/
將請求/www/xiaofang/xuexi/index.jsp
。
由於沒有定義path=""的Context組件,因此瀏覽器訪問http://www.xiaofang.com:8080
將請求/www/xiaofang/ROOT/index.jsp
。注意,是ROOT目錄。
如果加上<Context path="" docBase="" reloadable="true" />
,則訪問http://www.xiaofang.com:8080
將請求/www/xiaofang/index.jsp
。注意,不是ROOT目錄,而是相對於appBase的根目錄,即/www/xiaofang。
盡管本文解釋了一大堆關於appBase和docBase的設置,但一般都會采用大眾所熟知的配置方式:appBase設置為"webapps",即$CATALINA_HOME/webapps,而docBase設置為webapps下的webapp應用名。這樣配置不僅符合eclipse部署webapp時默認的部署目錄結構(eclipse部署應用時,將WebContent下的內容復制到docBase下,將servlet java源代碼編譯后的class文件復制到WEB-INF/classes目錄下),更利於維護webapp和相關配置。例如:
<Context docBase="MyWeb" path="/MyWeb" reloadable="true" />
<Context docBase="SecondWeb" path="/SecondWeb" reloadable="true" />
<Context docBase="WEB" path="/WEB" reloadable="true" />
但這樣的配置有個缺點,因為項目名稱一般都會帶有大寫字母,使得在瀏覽器訪問時,也要帶有大寫字母。例如輸入http://www.a.com/MyWeb/index.jsp
。因此,可采用另一種配置方式:設置Host的appBase為webapps下的某個目錄,然后在path上配置uri匹配路徑。如下:
<Host name="www.xiaofang.com" appBase="webapps/MyWeb" unpackWARs="true" autoDeploy="true">
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
<Context path="" docBase="" reloadable="true" />
</Host>
4. webapp目錄體系結構
webapp有特定的組織格式,是一種層次型目錄結構,通常包含了servlet代碼文件、jsp頁面文件、類文件、部署描述符文件等等。
這些文件可能是以目錄的形式存放,也可能會打包成各種歸檔格式的文件,如jar、war等。但jsp有規定,在web應用程序的根目錄下,一般要有下面幾個目錄:
- /WEB-INF:此webapp的私有資源目錄,從瀏覽器上是無法訪問此目錄資源的,通常web.xml放置於此目錄
- /WEB-INF/classes:此webapp自有的類
- /WEB-INF/lib:此webapp自有能夠打包為jar格式的類
- /META-INF:並非標准的webapp目錄,有的應用程序才有。當該應用程序想獨立定義自己的context.xml時可放入此目錄,也是私有目錄。
每個webapp要想被tomcat加載,一種方法是程序目錄放在$catalina.home/webapps下,另一種方式是配置該webapp相關的context配置,使tomcat能找到此webapp。正如前文所說,webapp目錄一般都會放在$catalina.home/webapps下。
簡單部署示例:
(1)對於war類歸檔程序:將歸檔文件復制到$CATALINA_BASE/webapps/目錄中,並重啟tomcat即可,tomcat會自動展開war歸檔。例如官方提供了一個sample.war作為tomcat學習初級示例( https://tomcat.apache.org/tomcat-8.5-doc/appdev/sample/sample.war ),下載后只需將其放入webapps下即可。
(2)在測試tomcat的過程中,很多時候是未歸檔程序,這時可以手動創建目錄來實現部署。需要創建的目錄有webapps/yourapp,此目錄下還要創建WEB-INF目錄,在WEB-INF目錄中還要創建classes和lib目錄。然后將jsp文件放在對應目錄下即可,如寫一個測試的index.jsp放在yourapp目錄下。而對於已經開發完畢的webapp,因為eclipse在發布測試webapp時已經設置好目錄,因此只要將webapp的目錄復制到webapps目錄下即可。
5. tomcat配置文件server.xml詳解
tomcat配置文件中配置的是各個組件的屬性,全局配置文件為$CATALINA_HOME/conf/server.xml,主要的組件有以下幾項:Server,Service,Connector,Engine,Host,Alias,Context,Valve等。配置完配置文件后需要重啟tomcat,但在啟動后一定要檢查tomcat是否啟動成功,因為即使出錯,很多時候它都不會報錯,可從監聽端口判斷。
配置方法見官方手冊,在頁面的左邊有各個組件的鏈接。
tomcat的配置文件都是xml文件,以下是xml文件的常見規則:
- 文件第一行設置xml標識,表示該文件是xml格式的文件。例如
<?xml version="1.0" encoding="UTF-8"?>
。 - xml文件的注釋方法為
<!-- XXX -->
,這可以是單行注釋,也可以多行注釋,只要前后注釋符號能對應上,中間的內容都是注釋。 - 定義屬性時有兩種方式:單行定義和多行定義。例如:
<!-- 單行定義的方式 -->
<NAME key=value />
<!-- 多行定義的方式 -->
<NAME key=value>
</NAME>
下面個組件的配置中有些地方使用了相對於$CATALINA_BASE的相對路徑,它和$CATALINA_HOME小有區別。如果只有一個tomcat實例,則它們是等價的,都是tomcat的安裝路徑。如果有多個tomcat實例,則$CATALINA_HOME表示的是安裝路徑,而$CATALINA_BASE表示的是各實例所在根目錄。關於tomcat多實例,見running.txt中對應的說明。
5.1 頂級元素server
server組件定義的是一個tomcat實例。默認定義如下:
<Server port="8005" shutdown="SHUTDOWN">
</Server>
它默認監聽在8005端口以接收shutdown命令。要啟用多個tomcat實例,將它們監聽在不同的端口即可。這個端口的定義為管理員提供一個關閉實例的便捷途徑,可以直接telnet至此端口使用SHUTDOWN命令關閉此實例。不過基於安全角度的考慮,通常不允許遠程進行。
Server的相關屬性:
className
:用於實現此組件的java類的名稱,這個類必須實現接口org.apache.catalina.Server。不給定該屬性時將采用默認的標准類org.apache.catalina.core.StandardServer;address
:監聽端口綁定的地址。如不指定,則默認為Localhost,即只能在localhost上發送SHUTDOWN命令;port
:接收shutdown指令的端口,默認僅允許通過本機訪問,默認為8005;shutdown
:通過TCP/IP連接發往此Server用於實現關閉tomcat實例的命令字符串。
在server組件中可嵌套一個或多個service組件。
5.2 頂級元素service
定義了service就能提供服務了。service組件中封裝connector和container,它同時也表示將此service中的connector和container綁定起來,即由它們組成一個service向外提供服務。默認定義如下:
<Service name="Catalina">
</Service>
Service相關的屬性:
className
:用於實現service的類名,這個類必須實現org.apache.catalina.Service接口。不給定該屬性時將采用默認的標准類org.apache.catalina.core.StandardService。name
:此service的顯示名稱,該名稱主要用於在日志中進行標識service。一般來說無關緊要,默認為Catalina。
5.3 執行器executor
執行器定義tomcat各組件之間共享的線程池。在以前,每個connector都會獨自創建自己的線程池,但現在,可以定義一個線程池,各組件都可以共享該線程池,不過主要是為各connector之間提供共享。注意,executor創建的是共享線程池,如果某個connector不引用executor創建的線程池,那么該connector仍會根據自己指定的屬性創建它們自己的線程池。
連接器必須要實現org.apache.catalina.Executor接口。它是一個嵌套在service組件中的元素,為了挑選所使用的connector,該元素還必須定義在connector元素之前。
默認的定義如下:
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4"/>
其中該組件的屬性有:
className
:用於實現此組件的java類的名稱,這個類必須實現接口org.apache.catalina.Executor。不給定該屬性時將采用默認的標准類org.apache.catalina.core.StandardThreadExecutor;name
:該線程池的名稱,其他組件需要使用該名稱引用該線程池。
標准類的屬性包括:
threadPriority
:線程優先級,默認值為5。daemon
:線程是否以daemon的方式運行,默認值為true。namePrefix
:執行器創建每個線程時的名稱前綴,最終線程的名稱為:namePrefix+threadNumber。maxThreads
:線程池激活的最大線程數量。默認值為200。minSpareThreads
:線程池中最少空閑的線程數量。默認值為25。maxIdleTime
:在空閑線程關閉前的毫秒數。除非激活的線程數量小於或等於minSpareThreads的值,否則會有空閑線程的出現。默認值為60000,即空閑線程需要保留1分鍾的空閑時間才被殺掉。maxQueueSize
:可執行任務的最大隊列數,達到隊列上限時的連接請求將被拒絕。prestartminSpareThreads
:在啟動executor時是否立即創建minSpareThreads個線程數,默認為false,即在需要時才創建線程。
例如在connector中指定所使用的線程池,方式如下:
<Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
5.4 連接器connector
連接器用於接收客戶端發送的請求並返回響應給客戶端。一個service中可以有多個connector。有多種connector,常見的為http/1.1,http/2和ajp(apache jserv protocol)。在tomcat中,ajp連接協議類型專用於tomcat前端是apache反向代理的情況下。
因此tomcat可以扮演兩種角色:
- Tomcat僅作為應用程序服務器:請求來自於前端的web服務器,這可能是Apache, IIS, Nginx等;
- Tomcat既作為web服務器,也作為應用程序服務器:請求來自於瀏覽器。
Tomcat應該考慮工作情形並為相應情形下的請求分別定義好需要的連接器才能正確接收來自於客戶端的請求。
此處暫先介紹HTTP/1.1連接器的屬性設置。ajp后文再做介紹。
HTTP連接器表示支持HTTP/1.1協議的組件。設置了該連接器就表示catalina啟用它的獨立web服務功能,當然,肯定也提供它必須的servlets和jsp執行功能。在一個service中可以配置一個或多個連接器,每個連接器都可以將請求轉發給它們相關聯的engine以處理請求、創建響應。
每個流入的請求都需要一個獨立的線程來接收。當並發請求數量超出maxThreads指定的值時,多出的請求將被堆疊在套接字中,直到超出acceptCount指定的值。超出accpetCount的請求將以"connection refused"錯誤進行拒絕。
默認的定義如下:
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
HTTP連接器的屬性實在太多,詳細配置方法見官方手冊。通常定義HTTP連接器時必須定義的屬性只有"port"。
address
:指定連接器監聽的地址,默認為所有地址,即0.0.0.0。maxThreads
:支持的最大並發連接數,默認為200;如果引用了executor創建的共享線程池,則該屬性被忽略。acceptCount
:設置等待隊列的最大長度;通常在tomcat所有處理線程均處於繁忙狀態時,新發來的請求將被放置於等待隊列中;maxConnections
:允許建立的最大連接數。acceptCount和maxThreads是接受連接的最大線程數。存在一種情況,maxConnections小於acceptCount時,超出maxConnections的連接請求將被接收,但不會與之建立連接。port
:監聽的端口,默認為0,此時表示隨機選一個端口,通常都應該顯式指定監聽端口。protocol
:連接器使用的協議,用於處理對應的請求。默認為HTTP/1.1,此時它會自動在基於Java NIO或APR/native連接器之間進行切換。定義AJP協議時通常為AJP/1.3。redirectPort
:如果某連接器支持的協議是HTTP,當接收客戶端發來的HTTPS請求時,則轉發至此屬性定義的端口。connectionTimeout
:等待客戶端發送請求的超時時間,單位為毫秒,默認為60000,即1分鍾;注意,這時候連接已經建立。keepAliveTimeout
:長連接狀態的超時時間。超出該值時,長連接將關閉。enableLookups
:是否通過request.getRemoteHost()進行DNS查詢以獲取客戶端的主機名;默認為true,應設置為false防止反解客戶端主機;compression
:是否壓縮數據。默認為off。設置為on時表示只壓縮text文本,設置為force時表示壓縮所有內容。應該在壓縮和sendfile之間做個權衡。useSendfile
:該屬性為NIO的屬性,表示是否啟用sendfile的功能。默認為true,啟用該屬性將會禁止compression屬性。
當協議指定為HTTP/1.1時,默認會自動在NIO/APR協議處理方式上進行按需切換。如要顯式指定協議,方式如下:
<connector port="8080" protocol="HTTP/1.1">
<connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol">
<connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol">
<connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol">
其中NIO是C/C++的非阻塞IO復用模型在JAVA中的IO實現,NIO2即AIO是異步NIO,即異步非阻塞IO:
NioProtocol :non blocking Java NIO connector
Nio2Protocol:non blocking Java NIO2 connector
AprProtocol :the APR/native connector
它們之間的異同點如下表所示:
Java Nio Connector | Java Nio2 Connector | APR/native Connector | |
---|---|---|---|
Classname | Http11NioProtocol | Http11Nio2Protocol | Http11AprProtocol |
Tomcat Version | 6.x onwards | 8.x onwards | 5.5.x onwards |
Support Polling | YES | YES | YES |
Polling Size | maxConnections | maxConnections | maxConnections |
Read Request Headers | Non Blocking | Non Blocking | Non Blocking |
Read Request Body | Blocking | Blocking | Blocking |
Write Response Headers and Body | Blocking | Blocking | Blocking |
Wait for next Request | Non Blocking | Non Blocking | Non Blocking |
SSL Support | Java SSL or OpenSSL | Java SSL or OpenSSL | OpenSSL |
SSL Handshake | Non blocking | Non blocking | Blocking |
Max Connections | maxConnections | maxConnections | maxConnections |
下面是一個定義了多個屬性的SSL連接器:
<Connector port="8443" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" acceptCount="100" debug="0" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" />
5.5 容器類engine
engine是service組件中用來分析協議的引擎機器,它從一個或多個connector上接收請求,並將請求交給對應的虛擬主機進行處理,最后返回完整的響應數據給connector,通過connector將響應數據返回給客戶端。
只有一個engine元素必須嵌套在每個service中,且engine必須在其所需要關聯的connector之后,這樣在engine前面的connector都可以被此engine關聯,而在engine后面的connector則被忽略,因為一個service中只允許有一個engine。
定義方式大致如下:
<Engine name="Catalina" defaultHost="localhost">
</Engine>
<Engine name="Standalone" defaultHost="localhost" jvmRoute="TomcatA">
</Engine>
常用的engine屬性有:
className
:實現engine的類,該類必須實現org.apache.catalina.Engine接口。不給定該屬性時將采用默認的標准類org.apache.catalina.core.StandardEngine。defaultHost
:指定處理請求的默認虛擬主機。在Engine中定義的多個虛擬主機的主機名稱中至少有一個跟defaultHost定義的主機名稱同名。name
:Engine組件的名稱,用於記錄日志和錯誤信息,無關緊要的屬性,可隨意給定。jvmRoute
:在啟用session粘性時指定使用哪種負載均衡的標識符。所有的tomcat server實例中該標識符必須唯一,它會追加在session標識符的尾部,因此能讓前端代理總是將特定的session轉發至同一個tomcat實例上。- 注意,jvmRoute同樣可以使用jvmRoute的系統屬性來設置。如果此處設置了jvmRoute,則覆蓋jvmRoute系統屬性。關於jvmRoute的使用,在后面tomcat ajp負載均衡的文章中介紹。
engine是容器中的頂級子容器,其內可以嵌套一個或多個Host作為虛擬主機,且至少一個host要和engine中的默認虛擬主機名稱對應。除了host,還可以嵌套releam和valve組件。
5.6 容器類host
host容器用來定義虛擬主機。engine從connector接收到請求進行分析后,會將相關的屬性參數傳遞給對應的(篩選方式是從請求首部的host字段和虛擬主機名稱進行匹配)虛擬host進行處理。如果沒有合適的虛擬主機,則傳遞給默認虛擬主機。因此每個容器中必須至少定義一個虛擬主機,且必須有一個虛擬主機和engine容器中定義的默認虛擬主機名稱相同。
大致定義方式如下:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
</Host>
常用屬性說明:
className
:實現host容器的類,該類必須實現org.apache.catalina.Host接口。不給定該屬性時將采用默認的標准類org.apache.catalina.core.StandardHost。name
:虛擬主機的主機名,忽略大小寫(初始化時會自動轉換為小寫)。可以使用前綴星號通配符,如"*.a.com"。使用了星號前綴的虛擬主機的匹配優先級低於精確名稱的虛擬主機。appBase
:此Host的webapps目錄,即webapp部署在此虛擬主機上時的存放目錄。包括非歸檔的web應用程序目錄和歸檔后的WAR文件的目錄。使用相對路徑時基於$CATALINA_BASE。xmlBase
:部署在此虛擬主機上的context xml目錄。startStopThreads
:啟動context容器時的並行線程數。如果使用了自動部署功能,則再次部署或更新時使用相同的線程池。autoDeploy
:在Tomcat處於運行狀態時放置於appBase目錄中的應用程序文件是否自動進行deploy或自動更新部署狀態。這等於同時開啟了deployOnStartup屬性和reload/redeploy webapp的功能。觸發自動更新時將默認重載該webapp。默認為true。unpackWars
:在執行此webapps時是否先對歸檔格式的WAR文件解壓再運行,設置為false時則直接執行WAR文件;默認為true。設置為false時會損耗性能。workDir
:該虛擬主機的工作目錄。每個webapp都有自己的臨時IO目錄,默認該工作目錄為$CATALINA_BASE/work。
大多數時候都只需設置虛擬主機名稱name和appBase屬性即可,其余采用默認,默認時會自動部署webapp。有時候還需要管理多個站點名稱,即主機別名。可以使用Alias為Host指定的主機名定義主機別名。如:
<Host name="web.a.com" appBase="webapps" unpackWARs="true">
<Alias>www.a.com</Alias>
</Host>
自動部署指的是自動裝載webapp以提供相關webapp的服務。
5.7 容器類context
connector和container是整個tomcat的心臟,而context則是container的心臟,更是tomcat心臟的心臟。它是真正管理servlet的地方,它的配置影響了servlet的工作方式。
一個context代表一個webapp。servlet中規定,每個webapp都必須基於已歸檔的WAR(WEB application archive)文件或基於非歸檔相關內容所在目錄。
catalina基於對請求URI與context中定義的path進行最大匹配前綴的規則進行挑選,從中選出使用哪個context來處理該HTTP請求。這相當於nginx的location容器,catalina的path就相當於location的path,它們的作用是相同的。
每個context都必須在虛擬主機容器host中有一個唯一的context name。context的path不需要唯一,因為允許同一個webapp不同版本的共存部署。此外,必須要有一個context的path為0長度的字符串(如<Context path="" docBase="ROOT"/>
),該context是該虛擬主機的默認webapp,用於處理所有無法被虛擬主機中所有context path匹配的請求(當然,不定義也可以,此時將自動隱式提供,見前文所述)。
關於context name,它是從context path推斷出來的,不僅如此,其余幾個屬性如context basefile name也是由此推斷出來的。規則如下:
- 如果path不為空,則context name等於context path,basefile name取path中去除前綴"/"后的路徑,且所有"/"替換為"#"。
- 如果path為空,則context name也為空,而basefile為ROOT(注意是大寫)。
例如:
context path context name basefile name deploy examples
-----------------------------------------------------------------
/foo /foo foo foo.xml,foo.war,foo
/foo/bar /foo/bar foo#bar foo#bar.xml,foo#bar.war,foo#bar
Empty String Empty String ROOT ROOT.xml,ROOT.war,ROOT
配置context時,強烈建議不要定義在server.xml中,因為定義在conf/server.xml中時,只能通過重啟tomcat來重載生效,也就是說無法自動部署應用程序了。雖說官方如此推薦,但大多數人出於習慣和方便,還是會直接寫在server.xml中,這並沒有什么問題,無非是重啟一下而已。
可以考慮定義在/META-INF/context.xml中,如果此時設置了copyXML屬性,在部署時會將此context.xml復制到$CATALINA_BASE/conf/enginename/hostname/下,並重命名為"basefile name.xml"。也可以直接定義在$CATALINA_BASE/conf/enginename/hostname/下的.xml文件中,該路徑的xml優先級高於/META-INF/context.xml。
還可以定義默認的context.xml文件,包括兩種:(1)定義在$CATALINA_BASE/conf/context.xml中,該默認context對所有webapp都生效;(2)定義在$CATALINA_BASE/conf/[enginename]/[hostname]/context.xml.default中,該默認context只對該虛擬主機中的所有webapp生效。
定義方式大致如下:
<Host name="www.a.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
<Context path="" docBase="ROOT"/>
<Context path="/bbs" docBase="web/bbs" reloadable="true"/>
</Host>
其中第一個context的path為空字符串,表示它是默認的context。當瀏覽器中輸入www.a.com時,由於無法匹配第二個context,所以被默認即第一個context處理,當瀏覽器中輸入www.a.com/bbs時,將被第二個context處理,它將執行web/bbs所對應的webapp,並返回相關內容。
在context容器中可以定義非常多的屬性,詳細內容見官方手冊,以下是常見的幾個屬性:
className
:實現host容器的類,該類必須實現org.apache.catalina.Context接口。不給定該屬性時將采用默認的標准類org.apache.catalina.core.StandardContext。cookies
:默認為true,表示啟用cookie來標識session。docBase
:即DocumentRoot,是該webapp的context root,即歸檔WAR文件所在目錄或非歸檔內容所在目錄。可以是絕對路徑,也可以是相對於該webapp appBase的相對路徑。path
:定義webapp path。注意,當path=""時,表示默認的context;另外只有在server.xml中才需要定義該屬性,其他所有情況下都不能定義該屬性,因為會根據docBase和context的xml文件名推斷出path。reloadable
:是否監控/WEB-INF/class和/WEB-INF/lib兩個目錄中文件的變化,變化時將自動重載。在測試環境下該屬性很好,但在真實生產環境部署應用時不應該設置該屬性,因為監控會大幅增加負載,因此該屬性的默認值為false。wrapperClass
:實現wrapper容器的類,wrapper用於管理該context中的servlet,該類必須實現org.apache.catalina.Wrapper接口,如果不指定該屬性則采用默認的標准類。xmlNamespaceAware
:和web.xml的解析方式有關。默認為true,設置為false可以提升性能。xmlValidation
:和web.xml的解析方式有關。默認為true,設置為false可以提升性能。
5.8 被嵌套類realm
realm定義的是一個安全上下文,就像是以哪種方式存儲認證時的用戶和組相關的數據庫。有多種方式可以實現數據存放:
- JAASRealm:基於Java Authintication and Authorization Service實現用戶認證;
- JDBCRealm:通過JDBC訪問某關系型數據庫表實現用戶認證;
- JNDIRealm:基於JNDI使用目錄服務實現認證信息的獲取;
- MemoryRealm:查找tomcat-user.xml文件實現用戶信息的獲取;
- UserDatabaseRealm:基於UserDatabase文件(通常是tomcat-user.xml)實現用戶認證,它實現是一個完全可更新和持久有效的MemoryRealm,因此能夠跟標准的MemoryRealm兼容;它通過JNDI實現;
下面是一個常見的使用UserDatabase的配置:
<Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
下面是一個使用JDBC方式獲取用戶認證信息的配置:
<Realm className="org.apache.catalina.realm.JDBCRealm" debug="99" driverName="org.gjt.mm.mysql.Driver" connectionURL="jdbc:mysql://localhost/authority" connectionName="test" connectionPassword="test" userTable="users" userNameCol="user_name" userCredCol="user_pass" userRoleTable="user_roles" roleNameCol="role_name" />
5.9 被嵌套類valve
Valve中文意思是閥門,類似於過濾器,它可以工作於Engine和Host/Context之間、Host和Context之間以及Context和Web應用程序的某資源之間。一個容器內可以建立多個Valve,而且Valve定義的次序也決定了它們生效的次序。
有多種不同的Valve:
- AccessLogValve:訪問日志Valve;
- ExtendedAccessValve:擴展功能的訪問日志Valve;
- JDBCAccessLogValve:通過JDBC將訪問日志信息發送到數據庫中;
- RequestDumperValve:請求轉儲Valve;
- RemoteAddrValve:基於遠程地址的訪問控制;
- RemoteHostValve:基於遠程主機名稱的訪問控制;
- SemaphoreValve:用於控制Tomcat主機上任何容器上的並發訪問數量;
- JvmRouteBinderValve:在配置多個Tomcat為以Apache通過mod_proxy或mod_jk作為前端的集群架構中,當期望停止某節點時,可以通過此Valve將用記請求定向至備用節點;使用此Valve,必須使用JvmRouteSessionIDBinderListener;
- ReplicationValve:專用於Tomcat集群架構中,可以在某個請求的session信息發生更改時觸發session數據在各節點間進行復制;
- SingleSignOn:將兩個或多個需要對用戶進行認證webapp在認證用戶時連接在一起,即一次認證即可訪問所有連接在一起的webapp;
- ClusterSingleSingOn:對SingleSignOn的擴展,專用於Tomcat集群當中,需要結合ClusterSingleSignOnListener進行工作;
其中RemoteHostValve和RemoteAddrValve可以分別用來實現基於主機名稱和基於IP地址的訪問控制,控制本身可以通過allow或deny來進行定義,這有點類似於Apache的訪問控制功能。如下面的Valve實現了僅允許本機訪問/probe:
<Context privileged="true" path="/probe" docBase="probe">
<Valve className="org.apache.catalina.valves.RemoteAddrValve" allow="127\.0\.0\.1"/>
</Context>
其中相關屬性定義有:
- className:在對應位置的后綴上加上".valves.RemoteHostValve"或".valves.RemoteAddrValve";
- allow:以逗號分開的允許訪問的IP地址列表,支持正則,點號“.”用於IP地址時需要轉義;僅定義allow項時,非明確allow的地址均被deny;
- deny: 以逗號分開的禁止訪問的IP地址列表,支持正則;使用方式同allow;僅定義deny項時,非明確deny的地址均被allow;
另外一個常用的Valve為AccessLogValve,定義方式大致如下:
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
其中prefix和suffix表示日志文件的前綴名稱和后綴名稱。pattern表示記錄日志時的信息和格式。