Tomcat處理HTTP請求原理&線程池

一.Tomcat是什麼?

Tomcat是一個Web應用服務器,同時也是一個Servlet/JSP容器。Tomcat做爲Servlet容器,負責處理客戶端請求,把請求傳送給Servlet,並將Servlet的響應返回給客戶端。html

 

三.Tomcat的兩個核心組件:Connector和Container
1.Connector組件

一 個Connector組件將在某個指定的端口上偵聽客戶請求,接收瀏覽器發過來的tcp鏈接請求,建立一個Request和一個Response對象分別 用於和其你去端交換數據,而後會產生一個線程來處理這個請求並把產生的Request和Response對象傳給Engine,從Engine中得到響應 並返回給客戶端。 Tomcat有兩個經典的Connector,一個直接偵聽來自瀏覽器的HTTP請求,另一個偵聽來自其餘的WebServer的請求。Cotote HTTP/1.1 Connector在端口8080處偵聽來自客戶瀏覽器的HTTP請求,Coyote JK2 Connector在端口8009處偵聽其餘WebServer的Servlet/JSP請求。 Connector 最重要的功能就是接收鏈接請求而後分配線程讓 Container來處理這個請求,因此這必然是多線程的,多線程的處理是 Connector 設計的核心。java

2.Container組件

Container組件的體系結構以下:web

Container

Container是容器的父接口,該容器的設計用的是典型的 責任鏈的設計模式,它由四個自容器組件構成,分別是Engine、Host、Context、Wrapper。這四個組件是負責關係,存在包含關係。一般 一個Servlet class對應一個Wrapper,若是有多個Servlet則定義多個Wrapper,若是有多個Wrapper就要定義一個更高的 Container,如Context。 Context定義在父容器 Host 中,其中Host 不是必須的,可是要運行 war 程序,就必需要 Host,由於 war 中必有 web.xml 文件,這個文件的解析就須要 Host 了,若是要有多個 Host 就要定義一個 top 容器 Engine 了。而 Engine 沒有父容器了,一個 Engine 表明一個完整的 Servlet 引擎。數據庫

Engine

Engine 容器比較簡單,它只定義了一些基本的關聯關係 Host 容器windows

Host

Host 是 Engine 的字容器,一個 Host 在 Engine 中表明一個虛擬主機,這個虛擬主機的做用就是運行多個應用,它負責安裝和展開這些應用,而且標識這個應用以便可以區分它們。它的子容器一般是 Context,它除了關聯子容器外,還有就是保存一個主機應該有的信息。設計模式

Context

Context 表明 Servlet 的 Context,它具有了 Servlet 運行的基本環境,理論上只要有 Context 就能運行 Servlet 了。簡單的 Tomcat 能夠沒有 Engine 和 Host。Context 最重要的功能就是管理它裏面的 Servlet 實例,Servlet 實例在 Context 中是以 Wrapper 出現的,還有一點就是 Context 如何才能找到正確的 Servlet 來執行它呢? Tomcat5 之前是經過一個 Mapper 類來管理的,Tomcat5 之後這個功能被移到了 request 中,在前面的時序圖中就能夠發現獲取子容器都是經過 request 來分配的瀏覽器

Wrapper

Wrapper 表明一個 Servlet,它負責管理一個 Servlet,包括的 Servlet 的裝載、初始化、執行以及資源回收。Wrapper 是最底層的容器,它沒有子容器了,因此調用它的 addChild 將會報錯。 Wrapper 的實現類是 StandardWrapper,StandardWrapper 還實現了擁有一個 Servlet 初始化信息的 ServletConfig,由此看出 StandardWrapper 將直接和 Servlet 的各類信息打交道。tomcat

說明:除了上述組件外,Tomcat中還有其餘重要的組件,如安全組件security、logger日誌組件、session、mbeans、naming等其餘組件。這些組件共同爲Connector和Container提供必要的服務。
四.Tomcat 處理一個HTTP請求的過程

 

1.用戶在瀏覽器中輸入網址localhost:8080/test/index.jsp,請求被髮送到本機端口8080,被在那裏監聽的Coyote HTTP/1.1 Connector得到;安全

2.Connector把該請求交給它所在的Service的Engine(Container)來處理,並等待Engine的迴應;服務器

3.Engine得到請求localhost/test/index.jsp,匹配全部的虛擬主機Host;

4.Engine 匹配到名爲localhost的Host(即便匹配不到也把請求交給該Host處理,由於該Host被定義爲該Engine的默認主機),名爲 localhost的Host得到請求/test/index.jsp,匹配它所擁有的全部Context。Host匹配到路徑爲/test的 Context(若是匹配不到就把該請求交給路徑名爲「 」的Context去處理);

5.path=「/test」的Context得到請求/index.jsp,在它的mapping table中尋找出對應的Servlet。Context匹配到URL Pattern爲*.jsp的Servlet,對應於JspServlet類;

6.構造HttpServletRequest對象和HttpServletResponse對象,做爲參數調用JspServlet的doGet()或doPost(),執行業務邏輯、數據存儲等;

7.Context把執行完以後的HttpServletResponse對象返回給Host;

8.Host把HttpServletResponse對象返回給Engine;

9.Engine把HttpServletResponse對象返回Connector;

10.Connector把HttpServletResponse對象返回給客戶Browser。

 --------------------------------------------------------------------------------------------------------------------------------------------------------------------

2、3個參數:acceptCount、maxConnections、maxThreads

再回顧一下Tomcat處理請求的過程:在accept隊列中接收鏈接(當客戶端向服務器發送請求時,若是客戶端與OS完成三次握手創建了鏈接,則OS將該鏈接放入accept隊列);在鏈接中獲取請求的數據,生成request;調用servlet容器處理請求;返回response。

相對應的,Connector中的幾個參數功能以下:

一、acceptCount

accept隊列的長度;當accept隊列中鏈接的個數達到acceptCount時,隊列滿,進來的請求一概被拒絕。默認值是100。

二、maxConnections

Tomcat在任意時刻接收和處理的最大鏈接數。當Tomcat接收的鏈接數達到maxConnections時,Acceptor線程不會讀取accept隊列中的鏈接;這時accept隊列中的線程會一直阻塞着,直到Tomcat接收的鏈接數小於maxConnections。若是設置爲-1,則鏈接數不受限制。

默認值與鏈接器使用的協議有關:NIO的默認值是10000,APR/native的默認值是8192,而BIO的默認值爲maxThreads(若是配置了Executor,則默認值是Executor的maxThreads)。

在windows下,APR/native的maxConnections值會自動調整爲設置值如下最大的1024的整數倍;如設置爲2000,則最大值實際是1024。

三、maxThreads

請求處理線程的最大數量。默認值是200(Tomcat7和8都是的)。若是該Connector綁定了Executor,這個值會被忽略,由於該Connector將使用綁定的Executor,而不是內置的線程池來執行任務。

maxThreads規定的是最大的線程數目,並非實際running的CPU數量;實際上,maxThreads的大小比CPU核心數量要大得多。這是由於,處理請求的線程真正用於計算的時間可能不多,大多數時間可能在阻塞,如等待數據庫返回數據、等待硬盤讀寫數據等。所以,在某一時刻,只有少數的線程真正的在使用物理CPU,大多數線程都在等待;所以線程數遠大於物理核心數纔是合理的。

換句話說,Tomcat經過使用比CPU核心數量多得多的線程數,可使CPU忙碌起來,大大提升CPU的利用率。

四、參數設置

(1)maxThreads的設置既與應用的特色有關,也與服務器的CPU核心數量有關。經過前面介紹能夠知道,maxThreads數量應該遠大於CPU核心數量;並且CPU核心數越大,maxThreads應該越大;應用中CPU越不密集(IO越密集),maxThreads應該越大,以便可以充分利用CPU。固然,maxThreads的值並非越大越好,若是maxThreads過大,那麼CPU會花費大量的時間用於線程的切換,總體效率會下降。

(2)maxConnections的設置與Tomcat的運行模式有關。若是tomcat使用的是BIO,那麼maxConnections的值應該與maxThreads一致;若是tomcat使用的是NIO,那麼相似於Tomcat的默認值,maxConnections值應該遠大於maxThreads。

(3)經過前面的介紹能夠知道,雖然tomcat同時能夠處理的鏈接數目是maxConnections,但服務器中能夠同時接收的鏈接數爲maxConnections+acceptCount 。acceptCount的設置,與應用在鏈接太高狀況下但願作出什麼反應有關係。若是設置過大,後面進入的請求等待時間會很長;若是設置太小,後面進入的請求立馬返回connection refused。

3、線程池Executor

Executor元素表明Tomcat中的線程池,能夠由其餘組件共享使用;要使用該線程池,組件須要經過executor屬性指定該線程池。

Executor是Service元素的內嵌元素。通常來講,使用線程池的是Connector組件;爲了使Connector能使用線程池,Executor元素應該放在Connector前面。Executor與Connector的配置舉例以下:

1
2
<Executor name= "tomcatThreadPool" namePrefix = "catalina-exec-" maxThreads= "150" minSpareThreads= "4" />
<Connector executor= "tomcatThreadPool" port= "8080" protocol= "HTTP/1.1" connectionTimeout= "20000" redirectPort= "8443" acceptCount= "1000" />

Executor的主要屬性包括:

  • name:該線程池的標記
  • maxThreads:線程池中最大活躍線程數,默認值200(Tomcat7和8都是)
  • minSpareThreads:線程池中保持的最小線程數,最小值是25
  • maxIdleTime:線程空閒的最大時間,當空閒超過該值時關閉線程(除非線程數小於minSpareThreads),單位是ms,默認值60000(1分鐘)
  • daemon:是否後臺線程,默認值true
  • threadPriority:線程優先級,默認值5
  • namePrefix:線程名字的前綴,線程池中線程名字爲:namePrefix+線程編號

4、查看當前狀態

上面介紹了Tomcat鏈接數、線程數的概念以及如何設置,下面說明如何查看服務器中的鏈接數和線程數。

查看服務器的狀態,大體分爲兩種方案:(1)使用現成的工具,(2)直接使用Linux的命令查看。

現成的工具,如JDK自帶的jconsole工具能夠方便的查看線程信息(此外還能夠查看CPU、內存、類、JVM基本信息等),Tomcat自帶的manager,收費工具New Relic等。下圖是jconsole查看線程信息的界面:

下面說一下如何經過Linux命令行,查看服務器中的鏈接數和線程數。

一、鏈接數

假設Tomcat接收http請求的端口是8083,則可使用以下語句查看鏈接狀況:

1
netstat –nat | grep 8083

二、線程

ps命令能夠查看進程狀態,如執行以下命令:

1
ps –e | grep java

結果以下圖:

能夠看到,只打印了一個進程的信息;27989是線程id,java是指執行的java命令。這是由於啓動一個tomcat,內部全部的工做都在這一個進程裏完成,包括主線程、垃圾回收線程、Acceptor線程、請求處理線程等等。

經過以下命令,能夠看到該進程內有多少個線程;其中,nlwp含義是number of light-weight process。

1
ps –o nlwp 27989

能夠看到,該進程內部有73個線程;可是73並無排除處於idle狀態的線程。要想得到真正在running的線程數量,能夠經過如下語句完成:

1
ps -eLo pid ,stat | grep 27989 | grep running | wc -l

其中ps -eLo pid ,stat能夠找出全部線程,並打印其所在的進程號和線程當前的狀態;兩個grep命令分別篩選進程號和線程狀態;wc統計個數。其中,ps -eLo pid ,stat | grep 27989輸出的結果以下:

圖中只截圖了部分結果;Sl表示大多數線程都處於空閒狀態。

---------------------------------------------------------------------------------------------------------------------------------------

相關文章
相關標籤/搜索