tomcat架構分析及配置詳解

女友在高考發表於2021-12-18

瀏覽器訪問伺服器的流程

請求發起的過程:

注意:瀏覽器訪問伺服器使用的是http協議,http是應用層協議,而具體傳輸還是使用的TCP/IP協議

Tomcat系統總架構

2.1 Tomcat請求處理過程

一個http請求過來,Tomcat會接收,然後根據配置呼叫不同的servlet來進行處理。

為了解耦,設計了一個Tomcat容器:

因此,Tomcat不僅是一個http伺服器,還是一個servlet容器。這也就是它的兩個重要的功能:

  1. 和客戶端瀏覽器互動,進行socket通訊,將位元組流和Request/Response物件進行轉換
  2. Servlet容器處理邏輯

Tomcat設計了兩個核心元件來完成這兩大功能

  1. 聯結器:處理Socket連線
  2. 容器:載入和管理Servlet

Servlet介面和Servlet容器這一整套內容叫做Servlet規範。

2.2 Tomcat Servlet容器處理流程

當使用者請求某個URL資源時

  1. Http伺服器會把請求資訊使用ServletRuest物件封裝起來
  2. 根據URL和Servlet的對映關係,找到對應的Servlet,進一步去某個具體的Servlet
  3. 如果Servlet還沒有被載入,就用反射機制建立這個Servlet,並呼叫Servlet的init方法來完成初始化
  4. 接著呼叫這個具體的Servlet的service方法來處理請求,請求處理結果使用ServletResponse物件封裝
  5. 把ServletResponse物件返回給HTTP伺服器,HTTP伺服器會把響應結果傳送給客戶端

Tomcat聯結器元件 Coyote(開椰踢)

3.1 Coyote簡介

Coyote是Tomcat中聯結器元件的名稱,是對外的介面。客戶端通過Coyote與伺服器建立連線,傳送請求並接收響應。

  1. Coyote封裝了底層的網路通訊(Socket請求及響應)
  2. Coyote使容器元件與具體的請求協議及IO操作完全解耦
  3. Coyote 將Socket輸入轉換封裝為Request物件,進一步封裝後交由Servlet的容器處理,請求處理完成後,容器通過Coyote提供的Response物件將結果寫入輸出流
  4. Coyote負責的是具體協議(應用層)和IO(傳輸層)相關內容

Tomcat支援多種應用層協議和IO模型:

名稱 應用層協議或IO模型 描述
應用層 HTTP/1.1 這時大部分Web應用採用的訪問協議
應用層 AJP 用於和WX整合(如Apache),以實現對靜態資源的優化以及叢集部署,當前支援AJP/1.3
應用層 HTTP/2 HTTP2大幅度提升了Web效能。下一代HTTP協議,自8.5以及9.0版本之後支援。
傳輸層 NIO 非阻塞I/O,採用Java NIO類庫實現
傳輸層 NIO2 非同步IO,採用JDK7 的NIO2類庫實現
傳輸層 APR 採用Apache可移植允許庫實現,是C/C++編寫的本地庫。如果選擇該方案,需要單獨安裝APR庫

Tomcat8.0 以前,Tomcat預設採用的IO方式為BIO,之後改為了NIO。無論是NIO、還是NIO2,效能都是優於以往的BIO,如果採用APR,甚至可以達到Apache HTTP Server的影響效能。

Coyote元件及作用

元件 作用
EndPoint EndPoint 是 Coyote 通訊端點,即通訊監聽的接⼝,是具體Socket接收和傳送處理器,是對傳輸層的抽象,因此EndPoint⽤來實現TCP/IP協議的
Processor Processor 是Coyote 協議處理接⼝ ,如果說EndPoint是⽤來實現TCP/IP協議的,那麼Processor⽤來實現HTTP協議,Processor接收來⾃EndPoint的Socket,讀取位元組流解析成Tomcat Request和Response物件,並通過Adapter將其提交到容器處理,Processor是對應⽤層協議的抽象
ProtocolHandler Coyote 協議接⼝, 通過Endpoint 和 Processor , 實現針對具體協議的處理能⼒。Tomcat 按照協議和I/O 提供了6個實現類 : AjpNioProtocol ,AjpAprProtocol, AjpNio2Protocol,Http11NioProtocol ,Http11Nio2Protocol ,Http11AprProtocol
Adapter 由於協議不同,客戶端發過來的請求資訊也不盡相同,Tomcat定義了⾃⼰的Request類來封裝這些請求資訊。ProtocolHandler接⼝負責解析請求並⽣成Tomcat Request類。但是這個Request物件不是標準的ServletRequest,不能⽤Tomcat Request作為引數來調⽤容器。Tomcat設計者的解決⽅案是引⼊CoyoteAdapter,這是介面卡模式的經典運⽤,聯結器調⽤CoyoteAdapter的Sevice⽅法,傳⼊的是Tomcat Request物件,CoyoteAdapter負責將Tomcat Request轉成ServletRequest,再調⽤容器

Tomcat Servlet容器 Catalina

Catalina是一款Servlet容器,且Catalina是Tomcat的核心。

它的結構如下:

其實,可以認為Tomcat就是一個Catalina例項,Tomcat啟動的時候會初始化這個例項,Catalina例項通過載入server.xml完成其他例項的建立,建立並管理一個Server,Server建立並管理多個服務,每個服務又可以有多個Connector和一個Container。

每個Service例項下可以有多個Connector例項和一個Container例項

Container元件的具體結構:

  • Engine

表示整個Catalina的Servlet引擎,用來管理多個虛擬站點,一個Service最多隻能有一個Engine,但是一個引擎可包含多個Host

  • Host

代表一個虛擬主機,或者說一個站點,可以給Tomcat配置多個虛擬主機地址,而一個虛擬主機下可包含多個Context

  • Context

表示一個Web應用程式,一個Web應用可包含多個Wrapper

  • Wrapper

表示一個Servlet,Wrapper作為容器中的最底層,不能包含子容器

Tomcat伺服器核心配置

核心配置:conf/server.xml檔案

主要結構如下:
一個Server根元素,有Listener、GlobalNamingResources、Service子標籤

<?xml version="1.0" encoding="UTF-8"?>

<Server port="8005" shutdown="SHUTDOWN">
  <Listener />
  
  <GlobalNamingResources></GlobalNamingResources>
 
  <Service name="Catalina"></Service>
 
</Server>

Server標籤詳細:

<!--
port:關閉伺服器的監聽端⼝
shutdown:關閉伺服器的指令字串
-->
<Server port="8005" shutdown="SHUTDOWN">
<!-- 以⽇志形式輸出伺服器 、作業系統、JVM的版本資訊 -->
<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 -->
<!-- 載入(伺服器啟動) 和 銷燬 (伺服器停⽌) APR。 如果找不到APR庫, 則會輸出⽇志, 並
不影響 Tomcat啟動 -->
<Listener className="org.apache.catalina.core.AprLifecycleListener"
SSLEngine="on" />
<!-- Prevent memory leaks due to use of particular java/javax APIs-->
<!-- 避免JRE記憶體洩漏問題 -->
<Listener
className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<!-- 載入(伺服器啟動) 和 銷燬(伺服器停⽌) 全域性命名服務 -->
<Listener
className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<!-- 在Context停⽌時重建 Executor 池中的執行緒, 以避免ThreadLocal 相關的記憶體洩漏 -->
<Listener
className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<!-- Global JNDI resources
Documentation at /docs/jndi-resources-howto.html
GlobalNamingResources 中定義了全域性命名服務
-->
<GlobalNamingResources>
<!-- Editable user database that can also be used by
UserDatabaseRealm to authenticate users
-->
<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>
<!-- 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">
...
</Service>
</Server>

Service標籤

<!--
該標籤⽤於建立 Service 例項,預設使⽤ org.apache.catalina.core.StandardService。
預設情況下,Tomcat 僅指定了Service 的名稱, 值為 "Catalina"。
Service ⼦標籤為 : Listener、Executor、Connector、Engine,
其中:
Listener ⽤於為Service新增⽣命週期監聽器,
Executor ⽤於配置Service 共享執行緒池,
Connector ⽤於配置Service 包含的連結器,
Engine ⽤於配置Service中連結器對應的Servlet 容器引擎
-->
<Service name="Catalina">
...
</Service>

Executor標籤

<!--
 預設情況下,Service 並未新增共享執行緒池配置。 如果我們想新增⼀個執行緒池, 可以在
<Service> 下新增如下配置:
name:執行緒池名稱,⽤於 Connector中指定
namePrefix:所建立的每個執行緒的名稱字首,⼀個單獨的執行緒名稱為
namePrefix+threadNumber
maxThreads:池中最⼤執行緒數
minSpareThreads:活躍執行緒數,也就是核⼼池執行緒數,這些執行緒不會被銷燬,會⼀直存在
maxIdleTime:執行緒空閒時間,超過該時間後,空閒執行緒會被銷燬,預設值為6000(1分鐘),單位
毫秒
maxQueueSize:在被執⾏前最⼤執行緒排隊數⽬,預設為Int的最⼤值,也就是⼴義的⽆限。除⾮特
殊情況,這個值 不需要更改,否則會有請求不會被處理的情況發⽣
prestartminSpareThreads:啟動執行緒池時是否啟動 minSpareThreads部分執行緒。預設值為
false,即不啟動
threadPriority:執行緒池中執行緒優先順序,預設值為5,值從1到10
className:執行緒池實現類,未指定情況下,預設實現類為
org.apache.catalina.core.StandardThreadExecutor。如果想使⽤⾃定義執行緒池⾸先需要實現
org.apache.catalina.Executor接⼝
-->
<Executor name="commonThreadPool"
namePrefix="thread-exec-"
maxThreads="200"
minSpareThreads="100"
maxIdleTime="60000"
maxQueueSize="Integer.MAX_VALUE"
prestartminSpareThreads="false"
threadPriority="5"
className="org.apache.catalina.core.StandardThreadExecutor"/>

Connector標籤

預設情況下,server.xml配置了兩個連結器,一個支援HTTP協議,一個支援AJP協議。大多數情況下,我們不需要新增連結器配置,只需要對已有的連結器進行優化

<!--
port:
端⼝號,Connector ⽤於建立服務端Socket 並進⾏監聽, 以等待客戶端請求連結。如果該屬性設定
為0, Tomcat將會隨機選擇⼀個可⽤的端⼝號給當前Connector 使⽤
protocol:
當前Connector ⽀持的訪問協議。 預設為 HTTP/1.1 , 並採⽤⾃動切換機制選擇⼀個基於 JAVA
NIO 的連結器或者基於本地APR的連結器(根據本地是否含有Tomcat的本地庫判定)
connectionTimeOut:
Connector 接收連結後的等待超時時間, 單位為 毫秒。 -1 表示不超時。
redirectPort:
當前Connector 不⽀持SSL請求, 接收到了⼀個請求, 並且也符合security-constraint 約束,
需要SSL傳輸,Catalina⾃動將請求重定向到指定的端⼝。
executor:
指定共享執行緒池的名稱, 也可以通過maxThreads、minSpareThreads 等屬性配置內部執行緒池。
URIEncoding:
⽤於指定編碼URI的字元編碼, Tomcat8.x版本預設的編碼為 UTF-8 , Tomcat7.x版本預設為ISO-
8859-1
-->
<!--org.apache.coyote.http11.Http11NioProtocol , ⾮阻塞式 Java NIO 連結器-->
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

可以使用共享執行緒池

<Connector port="8080"
protocol="HTTP/1.1"
executor="commonThreadPool"
maxThreads="1000"
minSpareThreads="100"
acceptCount="1000"
maxConnections="1000"
connectionTimeout="20000"
compression="on"
compressionMinSize="2048"
disableUploadTimeout="true"
redirectPort="8443"
URIEncoding="UTF-8" />

Engine 標籤

Engine表示Servlet引擎

<!--
name: ⽤於指定Engine 的名稱, 預設為Catalina
defaultHost:預設使⽤的虛擬主機名稱, 當客戶端請求指向的主機⽆效時, 將交由預設的虛擬主機處
理, 預設為localhost
-->
<Engine name="Catalina" defaultHost="localhost">
...
</Engine>

Host標籤

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

可以配置多個host訪問不同的目錄

<Host name="www.aaa.com"  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 &quot;%r&quot; %s %b" />

      </Host>
      <Host name="www.bbb.com"  appBase="webapps2"
            unpackWARs="true" autoDeploy="true">

        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>

Context 標籤

不配做Context的時候,appBase配置的目錄下的所有專案包都會被訪問,如想訪問appBase配置的目錄下的ROOT2,只需要localhost:8080/ROOT2

<Host name="www.abc.com" appBase="webapps" unpackWARs="true"
autoDeploy="true">
<!--
docBase:Web應⽤⽬錄或者War包的部署路徑。可以是絕對路徑,也可以是相對於 Host appBase的
相對路徑。
path:Web應⽤的Context 路徑。如果我們Host名為localhost, 則該web應⽤訪問的根路徑為:
http://localhost:8080/rt。
-->
<Context docBase="D:\apache-tomcat-8.5.32\ROOT" path="rt"/>
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t &quot;%r&quot; %s %b" />
</Host>

相關文章