1. 程式人生 > >Tomcat7.0原始碼分析——請求原理分析(上)

Tomcat7.0原始碼分析——請求原理分析(上)

前言

  談起Tomcat的誕生,最早可以追溯到1995年。近20年來,Tomcat始終是使用最廣泛的Web伺服器,由於其使用Java語言開發,所以廣為Java程式設計師所熟悉。很多早期的J2EE專案,由程式設計師自己實現Jsp頁面或者Servlet接受請求,後來藉助Struts1、Struts2、Spring等中介軟體後,實際也是利用Filter或者Servlet處理請求,大家肯定要問了,這些Servlet處理的請求來自哪裡?Tomcat作為Web伺服器是怎樣將HTTP請求交給Servlet的呢?

  本文就Tomcat對HTTP的請求處理細節進行分析。

Connector的初始化

  根據《Tomcat7.0原始碼分析——生命週期管理》一文的內容,我們知道Tomcat中有很多容器,包括Server、Service、Connector等。其中Connector正是與HTTP請求處理相關的容器。Service是Server的子容器,而Connector又是Service的子容器。那麼這三個容器的初始化順序為:Server->Service->Connector。Connector的實現分為以下幾種:

  • Http Connector:基於HTTP協議,負責建立HTTP連線。它又分為BIO Http Connector(是Tomcat的預設Connector)與NIO Http Connector兩種,後者提供對非阻塞IO與長連線Comet的支援。
  • AJP Connector:基於AJP協議,AJP是專門設計用於Tomcat與HTTP伺服器通訊定製的協議,能提供較高的通訊速度和效率。如與Apache伺服器整合時,採用這個協議。
  • APR HTTP Connector:用C實現,通過JNI呼叫的。主要提升對靜態資源(如HTML、圖片、CSS、JS等)的訪問效能。現在這個庫已獨立出來可用在任何專案中。APR效能較前兩類有很大提升。
現在我們直接來看Connector的initInternal方法吧,見程式碼清單1。
程式碼清單1
  1. @Override
  2. protectedvoid initInternal() 
    throws LifecycleException {  
  3.     super.initInternal();  
  4.     // Initialize adapter
  5.     adapter = new CoyoteAdapter(this);  
  6.     protocolHandler.setAdapter(adapter);  
  7.     IntrospectionUtils.setProperty(protocolHandler, "jkHome",  
  8.                                    System.getProperty("catalina.base"));  
  9.     onameProtocolHandler = register(protocolHandler,  
  10.             createObjectNameKeyProperties("ProtocolHandler"));  
  11.     mapperListener.setDomain(getDomain());  
  12.     onameMapper = register(mapperListener,  
  13.             createObjectNameKeyProperties("Mapper"));  
  14. }  

程式碼清單1說明了Connector的初始化步驟如下:

步驟一 構造網路協議處理的CoyoteAdapter

  程式碼清單1構造了CoyoteAdapter物件,並且將其設定為ProtocolHandler的Adapter。ProtocolHandler是做什麼的呢?Tomcat處理HTTP請求,需要有一個ServerSocket監聽網路埠來完成任務。介面ProtocolHandler被設計成控制網路埠監聽元件執行,負責元件的生命週期控制,這個介面實際並沒有定義網路埠監聽功能的規範,而是用於負責維護元件的生命週期。從ProtocolHandler的名字來看,它應該是網路協議的處理者,但它實際不負責這個功能,而是將其交給org.apache.coyote.Adapter來完成,這麼設計估計是為了方便維護和拓展新功能。Http11Protocol是ProtocolHandler介面的一個實現(是Connector的預設處理協議),被設計用來處理HTTP1.1網路協議的請求,通過該類可以完成在某個網路埠上面的監聽,同時以HTTP1.1的協議來解析請求內容,然後將請求傳遞到Connector所寄居的Container容器pipeline流水工作線上處理。此處的ProtocolHandler是何時生成的呢?還記得《Tomcat7.0原始碼分析——SERVER.XML檔案的載入與解析》一文中的Digester和Rule嗎?Digester在解析到標籤的時候,會執行startElement方法,startElement中會呼叫Rule的begin(String namespace, String name, Attributes attributes)方法,Connector對應的Rule就包括了ConnectorCreateRule。ConnectorCreateRule的begin方法的實現見程式碼清單2。

程式碼清單2

  1. @Override
  2. publicvoid begin(String namespace, String name, Attributes attributes)  
  3.         throws Exception {  
  4.     Service svc = (Service)digester.peek();  
  5.     Executor ex = null;  
  6.     if ( attributes.getValue("executor")!=null ) {  
  7.         ex = svc.getExecutor(attributes.getValue("executor"));  
  8.     }  
  9.     Connector con = new Connector(attributes.getValue("protocol"));  
  10.     if ( ex != null )  _setExecutor(con,ex);  
  11.     digester.push(con);  
  12. }  
程式碼清單2中呼叫了Connector的構造器,傳遞的引數為屬性protocol。我們知道server.xml中預設的Connector有兩個:
  1. <Connectorport="8080"protocol="HTTP/1.1"
  2.            connectionTimeout="20000"
  3.            redirectPort="8443"/>
  4. <!-- Define an AJP 1.3 Connector on port 8009 -->
  5. <Connectorport="8009"protocol="AJP/1.3"redirectPort="8443"/>

從server.xml可以看到兩個Connector都有屬性protocol。

Connector的構造器實現,見程式碼清單3。

程式碼清單3

  1. public Connector(String protocol) {  
  2.     setProtocol(protocol);  
  3.     // Instantiate protocol handler
  4.     try {  
  5.         Class<?> clazz = Class.forName(protocolHandlerClassName);  
  6.         this.protocolHandler = (ProtocolHandler) clazz.newInstance();  
  7.     } catch (Exception e) {  
  8.         log.error  
  9.             (sm.getString  
  10.              ("coyoteConnector.protocolHandlerInstantiationFailed", e));  
  11.     }  
  12. }  

setProtocol方法(見程式碼清單4)根據protocol引數的不同,呼叫setProtocolHandlerClassName方法(見程式碼清單5)設定protocolHandlerClassName屬性。以HTTP/1.1為例,由於預設情況下Apr不可用,所以protocolHandlerClassName會被設定為org.apache.coyote.http11.Http11Protocol,那麼反射生成的protocolHandler就是Http11Protocol例項。Tomcat預設還會配置協議是AJP/1.3的Connector,那麼此Connector的protocolHandler就是org.apache.coyote.ajp.AjpProtocol。 

程式碼清單4

  1. /** 
  2.  * Set the Coyote protocol which will be used by the connector. 
  3.  * 
  4.  * @param protocol The Coyote protocol name 
  5.  */
  6. publicvoid setProtocol(String protocol) {  
  7.     if (AprLifecycleListener.isAprAvailable()) {  
  8.         if ("HTTP/1.1".equals(protocol)) {  
  9.             setProtocolHandlerClassName  
  10.                 ("org.apache.coyote.http11.Http11AprProtocol");  
  11.         } elseif ("AJP/1.3".equals(protocol)) {  
  12.             setProtocolHandlerClassName  
  13.                 ("org.apache.coyote.ajp.AjpAprProtocol");  
  14.         } elseif (protocol != null) {  
  15.             setProtocolHandlerClassName(protocol);  
  16.         } else {  
  17.             setProtocolHandlerClassName  
  18.                 ("org.apache.coyote.http11.Http11AprProtocol");  
  19.         }  
  20.     } else {  
  21.         if ("HTTP/1.1".equals(protocol)) {  
  22.             setProtocolHandlerClassName  
  23.                 ("org.apache.coyote.http11.Http11Protocol");  
  24.         } elseif ("AJP/1.3".equals(protocol)) {  
  25.             setProtocolHandlerClassName  
  26.                 ("org.apache.coyote.ajp.AjpProtocol");  
  27.         } elseif (protocol != null) {  
  28.             setProtocolHandlerClassName(protocol);  
  29.         }  
  30.     }  
  31. }  

程式碼清單5

  1. publicvoid setProtocolHandlerClassName(String protocolHandlerClassName) {  
  2.     this.protocolHandlerClassName = protocolHandlerClassName;  
  3. }  

除此之外,ProtocolHandler還有其它實現,如圖1所示。


圖1  ProtocolHandler類繼承體系

圖1中有關ProtocolHandler的實現類都在org.apache.coyote包中 。前面所說的BIO Http Connector實際就是Http11Protocol,NIO Http Connector實際就是Http11NioProtocol,AJP Connector包括AjpProtocol和AjpAprProtocol,APR HTTP Connector包括AjpAprProtocol、Http11AprProtocol,此外還有一個MemoryProtocolHandler(這個是做什麼的,目前沒搞清楚,有知道的同學告訴我下啊!)。

步驟二 將ProtocolHandler、MapperListener註冊到JMX

  BIO Http Connector的ProtocolHandler(即Http11Protocol)的JMX註冊名為Catalina:type=ProtocolHandler,port=8080。BIO Http Connector的MapperListener的註冊名為Catalina:type=Mapper,port=8080。AJP Connector的ProtocolHandler(即AjpProtocol)的JMX註冊名為Catalina:type=ProtocolHandler,port=8009。AJP Connector的MapperListener的註冊名為Catalina:type=Mapper,port=8009。有關Tomcat中JMX註冊的內容,請閱讀《Tomcat7.0原始碼分析——生命週期管理》一文。

Connector的啟動

  根據《Tomcat7.0原始碼分析——生命週期管理》一文的內容,我們知道Tomcat中有很多容器。ProtocolHandler的初始化稍微有些特殊,Server、Service、Connector這三個容器的初始化順序為:Server->Service->Connector。值得注意的是,ProtocolHandler作為Connector的子容器,其初始化過程並不是由Connector的initInternal方法呼叫的,而是與啟動過程一道被Connector的startInternal方法所呼叫。由於本文的目的是分析請求,所以直接從Connector的startInternal方法(見程式碼清單6)開始。

程式碼清單6

相關推薦

Tomcat7.0原始碼分析——請求原理分析

前言   談起Tomcat的誕生,最早可以追溯到1995年。近20年來,Tomcat始終是使用最廣泛的Web伺服器,由於其使用Java語言開發,所以廣為Java程式設計師所熟悉。很多早期的J2EE專案,由程式設計師自己實現Jsp頁面或者Servlet接受請求,後

Spark原始碼分析之Spark Shell

https://www.cnblogs.com/xing901022/p/6412619.html 文中分析的spark版本為apache的spark-2.1.0-bin-hadoop2.7。 bin目錄結構: -rwxr-xr-x. 1 bigdata bigdata 1089 Dec

雲客Drupal8原始碼分析之外掛系統

各位《雲客drupal8原始碼分析》系列的讀者: 本系列一直以每週一篇的速度進行部落格原創更新,希望幫助大家理解drupal8底層原理,並縮短學習時間,但自《外掛系統(上)》主題開始部落格僅釋出前言和目錄,這是因為雲客在思考一個問題:drupal在國外如此流行但在國內卻很小

redis源碼分析之事務Transaction

訂閱 exe uri 興趣 閱讀 包含 如果 舉例 blog 這周學習了一下redis事務功能的實現原理,本來是想用一篇文章進行總結的,寫完以後發現這塊內容比較多,而且多個命令之間又互相依賴,放在一篇文章裏一方面篇幅會比較大,另一方面文章組織結構會比較亂,不容易閱讀。因此

資料分析----pandas 基本用法

一、生成資料表 1、首先匯入pandas庫,一般都會用到numpy庫,首先為我們先匯入備用 import numpy as np import pandas as pd 2、匯入CSV或者xlsx檔案 df=pd.DataFrame(pd.read_csv('name

資料分析報告怎麼寫

大資料和人工智慧迅猛擴充套件的時代,很多的企業崗位都需要進行資料分析。對於資料分析行業來說,做好資料分析是本職工作,不過在做好資料分析工作之後還需要會寫出一份清晰明瞭的資料分析報告。資料分析報告可以體現出一個數據分析師的綜合素質,那麼資料分析報告到底應該怎麼寫呢?下面好好看看! 一般來說,資料分析報告有

一站式學習Wireshark:應用Wireshark顯示過濾器分析特定資料流

介紹 掌握顯示過濾器對於網路分析者來說是一項必備的技能。這是一項大海撈針的技巧。學會構建,編輯,儲存關鍵的顯示過濾器能夠節省數小時的時間。 與捕捉過濾器使用的BPF語法不同,顯示過濾器使用的是Wireshark特定的格式。除了某些特例之外,Wireshark顯示過濾

Android記憶體分析和調優

PID    Vss        Rss        Pss       Uss      cmdline ...... 2319 42068K 42032K 13536K 7028K com.xxx ...... 該命令可以列出當前系統所有程序的記憶體佔用情況。 PID是程序ID。 Vss是佔用的虛擬

資料科學學習手札84基於geopandas的空間資料分析——空間計算篇

> 本文示例程式碼已上傳至我的`Github`倉庫[https://github.com/CNFeffery/DataScienceStudyNotes](https://github.com/CNFeffery/DataScienceStudyNotes) # 1 簡介   在本系列之前的文章中我們主要

高性能消息隊列 CKafka 核心原理介紹

幹貨 介紹 balance 拉取 這一 64bit 隱藏 2-2 kafka 歡迎大家前往騰訊雲技術社區,獲取更多騰訊海量技術實踐幹貨哦~ 作者:閆燕飛 1.背景 Ckafka是基礎架構部開發的高性能、高可用消息中間件,其主要用於消息傳輸、網站活動追蹤、運營監控、日誌聚

動態規劃原理講解

版權宣告:本文為博主原創文章,歡迎大家轉載,但是要註明我的文章地址。 https://blog.csdn.net/xyk_hust/article/details/83933341 大家好,歡迎大家閱讀動態規劃部分。由於本人水平有限,文中錯誤之處還請大家批評指正。 動態規劃分為兩個

Spark2.1.0模型設計與基本架構

  隨著近十年網際網路的迅猛發展,越來越多的人融入了網際網路——利用搜索引擎查詢詞條或問題;社交圈子從現實搬到了Facebook、Twitter、微信等社交平臺上;女孩子們現在少了逛街,多了在各大電商平臺上的購買;喜歡棋牌的人能夠在對戰平臺上找到世界各地的玩家對弈。在國內隨著網民數量的持續增加,造成網際網路公

【MySQL】我必須得告訴大家的MySQL優化原理2分割槽表

上一篇,後面留下兩個問題,這裡CHEN川  ^_^試著回答,讓我們看看大佬的看法吧? 前言: 如果資料量非常大的情況下,根據業務選擇了合適的欄位,精心設計了表和索引,仔細檢查了所有的SQL,確認沒有什麼問題了,但效能仍然不能滿足要求,這該怎麼辦?接下來討論一些常用的MyS

Tomcat 第四篇:請求處理流程

![](https://cdn.geekdigging.com/java/tomcat/tomcat_header.jpg) ## 1. 引言 既然是在講 Tomcat ,那麼一個 HTTP 請求的請求流程是無論如何也繞不開的。 首先拋開所有,使用我們現有的知識面,猜測一下一個請求被 Tomcat 處

Serilog 原始碼解析——資料的儲存

在[上一篇](https://www.cnblogs.com/iskcal/p/parse-of-string-template.html)中,我們主要研究了Serilog是如何解析字串模板的,它只是單獨對字串模板的處理,對於日誌記錄時所附帶的資料沒有做任何的操作。在本篇中,我們著重研究日誌資料的儲存方式。(

Spring AOP實現原理筆記 -- 原始碼分析

1、註冊AnnotationAwareAspectJAutoProxyCreator 首先要了解Spring解析XML配置檔案時,遇到自定義節點是如何解析的。可以參考Spring自定義XML標籤解析及其原理分析 當Spring遇到這個標籤的時候,它會

Tomcat7.0原始碼分析——server.xml檔案的載入與解析

前言作為Java程式設計師,對於Tomcat的server.xml想必都不陌生。本文基於Tomcat7.0的Java原始碼,對server.xml檔案是如何載入和解析進行分析。載入過程分析Bootstrap的load方法用於載入Tomcat的server.xml,實際是通過反

以太坊原始碼深入分析3-- 以太坊RPC通訊例項和原理程式碼分析

上一節提到,以太坊在node start的時候啟動了RPC服務,以太坊通過Rpc服務來實現以太坊相關介面的遠端呼叫。這節我們用個例項來看看以太坊 RPC是如何工作的,以及以太坊RPC的原始碼的實現一,RPC通訊例項1,RPC啟動命令 :geth --rpcgo-ethereu

Redis哨兵原理總結原始碼分析

目錄   本博文主要總結關於哨兵的一些理論知識,主要關注點有一下幾個方面: 一、哨兵解決了什麼問題? 二、哨兵是如何解決“問題一”的? 三、如何使用哨兵? 四、Redis Sentinel客戶端實現的原理是什麼?Java如何操作Redis Sentinel?

Vuex 2.0 原始碼分析

當我們用 Vue.js 開發一箇中到大型的單頁應用時,經常會遇到如下問題: 如何讓多個 Vue 元件共享狀態 Vue 元件間如何通訊 通常,在專案不是很複雜的時候,我們會利用全域性事件匯流排 (global event bus)解決,但是隨著複雜度的提升,這些程式碼將變的難以