首發CSDN:徐同學呀,原創不易,轉載請注明源連結。我是徐同學,用心輸出高品質文章,希望對你有所幫助。
本篇基于Tomcat10.0.6。
文章目錄
-
- 一、前言
- 二、Connector基本結構
- 三、ProtocolHandler
-
- 1、Endpoint通信端點
- 2、Processor應用層協定解析
-
- (1)ConnectionHandler建立合适的Processor
- (2)協定更新
- 3、配置方式
- 四、Adapter
- 五、請求處理完整流程
- 六、要點回顧
- 七、參考文獻
一、前言
Tomcat是一個非常優秀的開源項目,深深被它匠心獨具的架構理念所折服。技術的思維是互通的,哪有那麼多的高深莫測,都是對現實世界的抽象。
Tomcat作為Web應用伺服器,接收請求和處理請求是它的兩個核心功能。接收請求這步需要考慮适配多種網絡協定,主流的是
HTTP
協定,還有
WebSocket
協定、
AJP
協定等;處理請求就是找到對應的
Servlet
,執行業務代碼。
為了解耦和擴充性,Tomcat 設計了兩個核心元件來做這兩件事情:
- 連接配接器(
)負責對外交流,建立Connector
連接配接,讀取并解析網絡位元組流,生成Socket
和Request
對象并轉發給容器。Response
- 容器(
)負責内部處理,加載和管理Container
,通過Servlet
對象的輸出流寫入響應結果。Response
Tomcat連接配接器Connector源碼解讀(一)架構概覽,如何設計?為什麼這樣設計?
二、Connector基本結構
具體來看看
Connector
有哪些功能:
- 監聽網絡端口。
- 接受網絡連接配接請求。
- 讀取網絡請求位元組流。
- 根據具體應用層協定(
)解析位元組流,生成統一的HTTP/AJP
和Request
對象。(需要注意,Response
為了相容多種協定,沒有耦合Connector
标準,是以這裡生成的Servlet
和Request
對象沒有實作Response
和HttpServletRequest
接口)。HttpServletResponse
- 将
和Request
對象轉換為Response
和HttpServletRequest
對象。HttpServletResponse
- 将
和HttpServletRequest
對象轉發給HttpServletResponse
。Container
總結就是做了三件事:
- 網絡通信。
- 應用層協定解析和封裝。
-
和Request
對象轉換和傳遞。Response
對應
Connector
中的三個元件就是:
Endpoint
、
Processor
、
Adapter
,其中
Endpoint
和
Processor
放在一起抽象成了
ProtocolHandler
協定元件。
三、ProtocolHandler
ProtocolHandler
負責傳輸層網絡連接配接和應用層協定解析,由兩個核心部件
Endpoint
和
Processor
具體做這兩件事。
從實作類來看,Tomcat的連接配接器主要支援兩種協定:
HTTP/1.1
協定和
AJP
協定,實則還支援
HTTP/2.0
協定,
HTTP/2.0
的處理與
HTTP/1.1
和
AJP
不同,采用一種更新協定的方式實作。
-
協定:大家最熟悉的協定了,絕大多數web應用采用的通路協定,Tomcat既是HTTP/1.1
容器又是HTTP伺服器,單獨運作就可以對外提供服務,但是一般會搭配Servlet
web伺服器做反向代理和負載均衡。Nginx
-
協定:自Tomcat8.5開始支援,相較于HTTP/2.0
采用二進制傳輸資料而非文本格式;對消息頭采用HTTP/1.1
壓縮,提升傳輸效率;基于幀和流的多路複用,真正實作了基于一個連接配接多請求并發處理;支援伺服器主動推送。HPACK
-
協定:全名Apache JServ Protocol,是Alexei Kosut建立的定向包通信協定,采用二進制格式傳輸可讀文本。用于內建Web伺服器,以提升靜态資源的通路性能,目前最新版本為1.3。目前AJP/1.3
、Apache
、Tomcat
、Nginx
、Jetty
等均已支援JBoss
。(名不見經傳,不過多解釋)AJP
除了支援3種協定外,還分别支援3種I/O方式:
NIO
、
NIO2
、
APR
,Tomcat8.5之前預設還支援
BIO
,後來因為性能問題直接給删除了,APR也貼上了過期标簽。協定和I/O方式兩兩組合就出現了很多實作類:
Http11NioProtocol
、
Http11Nio2Protocol
、
Http11AprProtocol
(已過期)、
AjpNioProtocol
、
AjpNio2Protocol
、
AjpAjpProtocol
(已過期)。(
HTTP/2.0
是在
HTTP/1.1
的基礎上更新處理的,不在該繼承體系内,
APR
采用
Apache
可移植運作庫(
c++
編寫的本地庫,就是
native
方法)實作的,官方已标注過期不建議使用,是以不過多解釋)
Tomcat采用
UpgradeProtocol
表示HTTP更新協定,目前隻有一個實作類
Http2Protocol
用于處理
HTTP/2.0
。它根據請求建立一個用于更新處理的令牌
UpgradeToken
,該令牌包含了具體的HTTP更新處理器
HttpUpgradeHandler
(
HTTP/2.0
的處理器為
Http2UpgradeHandler
)。(Tomcat中
WebSocket
也是通過
UpgradeToken
機制實作的,其處理器為
WsHttpUpgradeHandler
)
1、Endpoint通信端點
Endpoint
負責網絡通信,監聽一個端口,循環接收
socket
請求,讀取網絡位元組流等。
Endpoint
不是接口,而是提供了一個抽象類
AbstractEndpoint
,又根據I/O方式提供了若幹實作類:
網絡通信這一層是非常抽象的,傳輸層協定毋庸置疑是
TCP/IP
,但是如何監聽socket請求,讀取網絡位元組流的方式是多樣的,同步非阻塞、I/O多路複用、異步非阻塞等等:
-
高度抽象出一個Endpoint
來循環監聽Acceptor
請求;socket
- Tomcat是支援高并發的,但是機器的性能是有限的,為了保證Web伺服器不被高流量沖垮,是以在接收請求前會有一個
限流器(利用LimitLatch
實作);AQS
- 接收到的
通道(socket
orSocketChannel
),會先根據I/O方式包裝一下,如同步非阻塞AsynchronousSocketChannel
、異步非阻塞NioChannel
等;Nio2Channel
- 包裝之後的
通道又用一個更抽象的socket
封裝,以應對不同方式的網絡位元組流的讀取和寫入;SocketWrapper
- 出于高并發的設計,将抽象的
交由SocketWrapper
任務對象處理,SocketProcessor
會扔進一個線程池SocketProcessor
處理;Executor
- 為了進一步提高性能,每次處理請求都會把一些對象緩存起來,不重複建立,比如
、SocketWrapper
等。SocketProcessor
2、Processor應用層協定解析
Acceptor
接收到請求封裝成一個
SocketProcessor
扔進線程池
Executor
後,會調用
Processor
從作業系統底層讀取、過濾位元組流,對應用層協定(
HTTP/AJP
)進行解析封裝,生成
org.apache.coyote.Request
和
org.apache.coyote.Response
對象。不同的協定有不同的
Processor
,
HTTP/1.1
對應
Http11Processor
,
AJP
對應
AjpProcessor
,
HTTP/1.2
對應
StreamProcessor
,
UpgradeProcessorInternal
和
UpgradeProcessorExternal
用于協定更新:
(1)ConnectionHandler建立合适的Processor
SocketProcessor
并不是直接調用的
Processor
,而是通過
org.apache.coyote.AbstractProtocol.ConnectionHandler#process
找到一個合适的
Processor
進行請求處理:
- 根據不同協定建立
orHttp11Processor
;AjpProcessor
- 根據協定更新是内部更新(
)還是外部更新建立HTTP/2.0
orUpgradeProcessorInternal
。UpgradeProcessorExternal
(2)協定更新
如果是正常的協定,如
HTTP/1.1
、
AJP/1.3
,則
Processor#process
處理完請求後會直接調用
Adapter#service
,将請求轉發給
Container
。
如果是協定更新(除
Websocket
),首先通過HTTP/1.1進行協定更新:
- 伺服器接收到帶有特殊請求頭(
)的Upgrade
連接配接,是以仍會先交給HTPP/1.1
進行處理;Http11Processor
- 根據請求頭
對應協定名建立Upgrade
,并指派給目前UpgradeToken
;Processor
- 傳回
,再由SocketState.UPGRADING
進行協定更新;ConnectionHandler
-
會從目前ConnectionHandler
擷取Processor
對象(如果沒有,則預設為HTTP/2.0),并建構一個更新的UpgradeToken
(若為Tomcat可以處理的協定更新(Processer
、HTTP/2.0
) ,則是WebSocket
,否則為UpgradeProcessorInternal
)。UpgradeProcessorExternal
- 替換目前
,并将目前Processer
釋放回收;Processer
- 将
設定給UpgradeProcessor
中的UpgradeToken
,并調用HttpUpgradeHandler
進行初始化,開啟更新協定的處理。HttpUpgradeHandler.init
- 由于
是多路複用協定,一個連接配接可以處理多個HTTP請求,是以對于HTTP/2.0
,會将每次請求響應交于Http2UpgradeHandler
處理,再由StreamProcessor
将請求送出給StreamProcessor
。Container
注意:
-
和UpgradeProcessorInternal
都實作了接口UpgradeProcessorExternal
,表示一個用于更新的連接配接,并不處理協定更新後資料讀寫和解析,而是交由WebConnection
,對于HttpUpgradeHandler
再建構Http2UpgradeHandler
,又将StreamProcessor
包裝成任務類StreamProcessor
,扔進線程池StreamRunnable
處理。Executor
-
也屬于協定更新,和WebSocket
更新方案一緻,但是協定更新的判斷機制有所不同,HTTP/2.0
更新判斷不在連接配接器裡,而是交由WebSocket
容器通過目前請求的過濾器Servlet
判斷,如果是WsFilter
協定更新,則調用目前WebSocket
建構org.apache.catalina.connector.Request#upgrade
并傳遞給UpgradeToken
處理(調用鈎子函數Http11Processor
),之後到了org.apache.coyote.AbstractProcessor#action
邏輯就跟ConnectionHandler
更新差不多了。HTTP/2.0
3、配置方式
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
executor="tomcatThreadPool"
redirectPort="8443">
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol"/>
</Connector>
-
是port
監聽的端口。Connector
-
是應用層協定名,可填參數有protocol
、HTTP/1.1
、org.apache.coyote.http11.Http11NioProtocol
、AJP/1.3
,如果org.apache.coyote.ajp.AjpNioProtocol
不填,則預設為protocol
。Http11NioProtocol
-
表示connectionTimeout
接收到連接配接後等待逾時時間,機關毫秒,預設20秒。Connector
-
表示使用一個共享線程池,若使用私有線程池,則executor
不需要指定,私有線程池可選參數有executor
、minSpareThreads=“10”
等maxThreads=“200”
-
表示非redirectPort
重定向到SSL
端口,當請求是SSL
請求,但是接收到的請求内容需要non-SSL
傳輸,則重定向到SSL
端口。SSL
- 若為
開啟HTTP
支援,需要配置HTTP/2.0
。UpgradeProtocol
Tomcat連接配接器Connector源碼解讀(一)架構概覽,如何設計?為什麼這樣設計?
四、Adapter
Adapter
接口隻有一個實作類
org.apache.catalina.connector.CoyoteAdapter
,其主要職責如下:
- 将
和org.apache.coyote.Request
轉為實作了标準org.apache.coyote.Response
的Servlet
和org.apache.catalina.connector.Request
。org.apache.catalina.connector.Response
- 将請求體的
、serverName
、URI
傳給version
元件做映射,比對到合适的Mapper
、Host
、Context
。Wrapper
- 将
和Request
傳給Response
處理,Container
通過管道Engine
傳給Pipeline
,Host
再傳給Host
,Context
再傳給Context
,Wrapper
是最終的Wrapper
。Servlet
五、請求處理完整流程
六、要點回顧
任重而道遠啊,本篇主要從
Connector
的整體架構進行講解,主要弄明白
Connector
是如何設計的?為什麼要這樣設計?
Connector
作為獨立子產品,封裝底層網絡通信,使
Container
和具體的協定及I/O方式解耦,易擴充、高性能。這也使得架構變得抽象複雜,
Connector
需要應對多種協定和I/O方式的組合,高度的抽象和封裝。
Connector
隻負責接收和解析請求,具體的業務處理還需要交給
Container
,是以需要一個擴充卡作為
Connector
和
Container
連接配接的橋梁。
很多實作細節并沒有展開,比如:
-
的工作原理沒有展開,如何監聽請求?如何限流?如何封裝接收到的Endpoint
?如何将連接配接扔進線程池裡處理?socket
-
應用層協定解析原理沒有展開,如何讀取底層位元組流?如何解析封裝應用層協定?如何封裝生成Processor
和Request
?Response
- 擴充卡
如何做轉發?如何找到對應的Adapter
、Host
、Context
。Wrapper
涉及的知識點很多,比如:
- 對傳輸層協定
的了解。TCP/IP
- 對I/O模型的了解(I/O多路複用、同步非阻塞、異步非阻塞等)。
- 對傳輸層協定的了解(HTTP協定真的要掌握,不然真看不懂代碼是怎麼解析請求行、請求頭、請求體的)。
- 高并發架構設計(緩存、
響應式、傳輸層協定延遲加載)。Reactor
後續會一一講解。
七、參考文獻
- 書籍:《Tomcat架構解析》劉光瑞(Tomcat8.5)
- 書籍:《Tomcat核心設計剖析》汪建(Tomcat7)
- 極客時間:《深入拆解Tomcat & Jetty》李号雙(Tomcat9.x)
- Tomcat源碼:https://gitee.com/stefanpy/tomcat-source-code-learning (Tomcat10.0.6)
如若文章有錯誤了解,歡迎批評指正,同時非常期待你的留言和點贊。如果覺得有用,不妨點個在看,讓更多人受益。