1. 程式人生 > >tomcat 執行緒模型

tomcat 執行緒模型

最近看到了內網ATA上的一篇斷網故障時Mtop觸發tomcat高併發場景下的BUG排查和修復(已被apache採納),引起了我的好奇,感覺原作者對應底層十分了解,寫的很複雜。原來對於tomcat的執行緒模型不怎麼清楚,但是它又是我們日常最常用的伺服器,於是我對它的執行緒模型進行了補習。

一. tomcat支援的請求處理方式

Tomcat支援三種接收請求的處理方式:BIO、NIO、APR

  1. BIO模式:阻塞式I/O操作,表示Tomcat使用的是傳統Java I/O操作(即Java.io包及其子包)。Tomcat7以下版本預設情況下是以bio模式執行的,由於每個請求都要建立一個執行緒來處理,執行緒開銷較大,不能處理高併發的場景,在三種模式中效能也最低。啟動tomcat看到如下日誌,表示使用的是BIO模式:
    這裡寫圖片描述

  2. NIO模式:是java SE 1.4及後續版本提供的一種新的I/O操作方式(即java.nio包及其子包)。是一個基於緩衝區、並能提供非阻塞I/O操作的Java API,它擁有比傳統I/O操作(bio)更好的併發執行效能。在tomcat 8之前要讓Tomcat以nio模式來執行比較簡單,只需要在Tomcat安裝目錄/conf/server.xml檔案中將如下配置:

<Connector port="8080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />

修改成

<Connector port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol"connectionTimeout="20000"redirectPort="8443" />

Tomcat8以上版本,預設使用的就是NIO模式,不需要額外修改
這裡寫圖片描述

  1. apr模式:簡單理解,就是從作業系統級別解決非同步IO問題,大幅度的提高伺服器的處理和響應效能, 也是Tomcat執行高併發應用的首選模式。
    啟用這種模式稍微麻煩一些,需要安裝一些依賴庫,下面以在CentOS7 mini版環境下Tomcat-8.0.35為例,介紹安裝步聚:
APR 1.2+ development headers (libapr1-dev package
) OpenSSL 0.9.7+ development headers (libssl-dev package) JNI headers from Java compatible JDK 1.4+ GNU development environment (gcc, make)

二. tomcat的NioEndpoint

我們先來簡單回顧下目前一般的NIO伺服器端的大致實現,借鑑infoq上的一篇文章Netty系列之Netty執行緒模型中的一張圖
這裡寫圖片描述

一個或多個Acceptor執行緒,每個執行緒都有自己的Selector,Acceptor只負責accept新的連線,一旦連線建立之後就將連線註冊到其他Worker執行緒中。
多個Worker執行緒,有時候也叫IO執行緒,就是專門負責IO讀寫的。一種實現方式就是像Netty一樣,每個Worker執行緒都有自己的Selector,可以負責多個連線的IO讀寫事件,每個連線歸屬於某個執行緒。另一種方式實現方式就是有專門的執行緒負責IO事件監聽,這些執行緒有自己的Selector,一旦監聽到有IO讀寫事件,並不是像第一種實現方式那樣(自己去執行IO操作),而是將IO操作封裝成一個Runnable交給Worker執行緒池來執行,這種情況每個連線可能會被多個執行緒同時操作,相比第一種併發性提高了,但是也可能引來多執行緒問題,在處理上要更加謹慎些。tomcat的NIO模型就是第二種。

這就要詳細瞭解下tomcat的NioEndpoint實現了。先來借鑑看下 斷網故障時Mtop觸發tomcat高併發場景下的BUG排查和修復(已被apache採納) 中的一張圖
這裡寫圖片描述
這張圖勾畫出了NioEndpoint的大致執行流程圖,worker執行緒並沒有體現出來,它是作為一個執行緒池不斷的執行IO讀寫事件即SocketProcessor(一個Runnable),即這裡的Poller僅僅監聽Socket的IO事件,然後封裝成一個個的SocketProcessor交給worker執行緒池來處理。下面我們來詳細的介紹下NioEndpoint中的Acceptor、Poller、SocketProcessor。
它們處理客戶端連線的主要流程如圖所示:
這裡寫圖片描述
圖中Acceptor及Worker分別是以執行緒池形式存在,Poller是一個單執行緒。注意,與BIO的實現一樣,預設狀態下,在server.xml中沒有配置<Executor>,則以Worker執行緒池執行,如果配置了<Executor>,則以基於java concurrent 系列的java.util.concurrent.ThreadPoolExecutor執行緒池執行。

  1. Acceptor
    接收socket執行緒,這裡雖然是基於NIO的connector,但是在接收socket方面還是傳統的serverSocket.accept()方式,獲得SocketChannel物件,然後封裝在一個tomcat的實現類org.apache.tomcat.util.net.NioChannel物件中。然後將NioChannel物件封裝在一個PollerEvent物件中,並將PollerEvent物件壓入events queue裡。這裡是個典型的生產者-消費者模式,Acceptor與Poller執行緒之間通過queue通訊,Acceptor是events queue的生產者,Poller是events queue的消費者。

  2. Poller
    Poller執行緒中維護了一個Selector物件,NIO就是基於Selector來完成邏輯的。在connector中並不止一個Selector,在socket的讀寫資料時,為了控制timeout也有一個Selector,在後面的BlockSelector中介紹。可以先把Poller執行緒中維護的這個Selector標為主Selector。 Poller是NIO實現的主要執行緒。首先作為events queue的消費者,從queue中取出PollerEvent物件,然後將此物件中的channel以OP_READ事件註冊到主Selector中,然後主Selector執行select操作,遍歷出可以讀資料的socket,並從Worker執行緒池中拿到可用的Worker執行緒,然後將socket傳遞給Worker。整個過程是典型的NIO實現。

  3. Worker
    Worker執行緒拿到Poller傳過來的socket後,將socket封裝在SocketProcessor物件中。然後從Http11ConnectionHandler中取出Http11NioProcessor物件,從Http11NioProcessor中呼叫CoyoteAdapter的邏輯,跟BIO實現一樣。在Worker執行緒中,會完成從socket中讀取http request,解析成HttpServletRequest物件,分派到相應的servlet並完成邏輯,然後將response通過socket發回client。在從socket中讀資料和往socket中寫資料的過程,並沒有像典型的非阻塞的NIO的那樣,註冊OP_READ或OP_WRITE事件到主Selector,而是直接通過socket完成讀寫,這時是阻塞完成的,但是在timeout控制上,使用了NIO的Selector機制,但是這個Selector並不是Poller執行緒維護的主Selector,而是BlockPoller執行緒中維護的Selector,稱之為輔Selector。

三. tomcat8的併發引數控制

本篇的tomcat版本是tomcat8.5。可以到這裡看下tomcat8.5的配置引數

  1. acceptCount
    文件描述為:
    The maximum queue length for incoming connection requests when all possible request processing threads are in use. Any requests received when the queue is full will be refused. The default value is 100.
    這個引數就立馬牽涉出一塊大內容:TCP三次握手的詳細過程,這個之後再詳細探討。這裡可以簡單理解為:連線在被ServerSocketChannel accept之前就暫存在這個佇列中,acceptCount就是這個佇列的最大長度。ServerSocketChannel accept就是從這個佇列中不斷取出已經建立連線的的請求。所以當ServerSocketChannel accept取出不及時就有可能造成該佇列積壓,一旦滿了連線就被拒絕了

  2. acceptorThreadCount
    文件如下描述
    The number of threads to be used to accept connections. Increase this value on a multi CPU machine, although you would never really need more than 2. Also, with a lot of non keep alive connections, you might want to increase this value as well. Default value is 1.
    Acceptor執行緒只負責從上述佇列中取出已經建立連線的請求。在啟動的時候使用一個ServerSocketChannel監聽一個連線埠如8080,可以有多個Acceptor執行緒併發不斷呼叫上述ServerSocketChannel的accept方法來獲取新的連線。引數acceptorThreadCount其實使用的Acceptor執行緒的個數。

  3. maxConnections
    文件描述如下
    The maximum number of connections that the server will accept and process at any given time. When this number has been reached, the server will accept, but not process, one further connection. This additional connection be blocked until the number of connections being processed falls below maxConnections at which point the server will start accepting and processing new connections again. Note that once the limit has been reached, the operating system may still accept connections based on the acceptCount setting. The default value varies by connector type. For NIO and NIO2 the default is 10000. For APR/native, the default is 8192.
    Note that for APR/native on Windows, the configured value will be reduced to the highest multiple of 1024 that is less than or equal to maxConnections. This is done for performance reasons. If set to a value of -1, the maxConnections feature is disabled and connections are not counted.
    這裡就是tomcat對於連線數的一個控制,即最大連線數限制。一旦發現當前連線數已經超過了一定的數量(NIO預設是10000),上述的Acceptor執行緒就被阻塞了,即不再執行ServerSocketChannel的accept方法從佇列中獲取已經建立的連線。但是它並不阻止新的連線的建立,新的連線的建立過程不是Acceptor控制的,Acceptor僅僅是從佇列中獲取新建立的連線。所以當連線數已經超過maxConnections後,仍然是可以建立新的連線的,存放在上述acceptCount大小的佇列中,這個佇列裡面的連線沒有被Acceptor獲取,就處於連線建立了但是不被處理的狀態。當連線數低於maxConnections之後,Acceptor執行緒就不再阻塞,繼續呼叫ServerSocketChannel的accept方法從acceptCount大小的佇列中繼續獲取新的連線,之後就開始處理這些新的連線的IO事件了。

  4. maxThreads
    文件描述如下
    The maximum number of request processing threads to be created by this Connector, which therefore determines the maximum number of simultaneous requests that can be handled. If not specified, this attribute is set to 200. If an executor is associated with this connector, this attribute is ignored as the connector will execute tasks using the executor rather than an internal thread pool.
    這個簡單理解就算是上述worker的執行緒數。他們專門用於處理IO事件,預設是200。

相關推薦

tomcat 執行模型

最近看到了內網ATA上的一篇斷網故障時Mtop觸發tomcat高併發場景下的BUG排查和修復(已被apache採納),引起了我的好奇,感覺原作者對應底層十分了解,寫的很複雜。原來對於tomcat的執行緒模型不怎麼清楚,但是它又是我們日常最常用的伺服器,於是我對它

tomcat的NIO執行模型原始碼分析

1 tomcat8的併發引數控制 這種問題其實到官方文件上檢視一番就可以知道,tomcat很早的版本還是使用的BIO,之後就支援NIO了,具體版本我也不記得了,有興趣的自己可以去查下。本篇的tomcat版本是tomcat8.5。可以到這裡看下tomcat8.5的配置引數 我們先來簡單回顧下目前一般的N

Tomcat 7伺服器執行模型

Tomcat 7伺服器網路處理主要由NioEndpoint,其處理客戶端連線的主要流程如圖所示圖中Acceptor及Worker分別是以執行緒池形式存在,Poller是一個單執行緒。注意,與BIO的實現一樣,預設狀態下,在server.xml中沒有配置<Executo

tomcat執行模型

現在考慮這樣一個情景。你要做一個高效能的通訊程式,現在按照傳統方式開始做。客戶端就不寫了。伺服器端的程式碼大致如下://建立一個伺服器端socketServerSocket socket = new ServerSocket(port,backlog);//開始監聽while

Tomcat執行模型淺析

我們在上一部分文章裡已經看到了,Tomcat的架構是如何一步步構建出來,但是在後臺伺服器的構建中,一個很重要的問題是如何實現多執行緒?一般情況下,如果我們來實現最初步的想法就是:不斷迴圈接收客戶端的連線,每個連線構建一個執行緒,然後進行相關的資料處理!但是

從聯結器元件看Tomcat執行模型——聯結器簡介

## Connector元件介紹 Connector(聯結器)元件是Tomcat最核心的兩個元件之一,主要的職責是負責接收客戶端連線和客戶端請求的處理加工。每個Connector都將指定一個埠進行監聽,分別負責對請求報文解析和對響應報文組裝。 Connector元件是整個Tomcat的入口,假如我們想要學

從聯結器元件看Tomcat執行模型——BIO模式

在高版本的Tomcat中,預設的模式都是使用NIO模式,在Tomcat 9中,BIO模式的實現Http11Protocol甚至都已經被刪除了。但是瞭解BIO的工作機制以及其優缺點對學習其他模式有有幫助。只有對比後,你才能知道其他模式的優勢在哪裡。 Http11Protocol表示阻塞式的HTTP協議的通訊,

從聯結器元件看Tomcat執行模型——NIO模式

Tomcat8之後,針對Http協議預設使用org.apache.coyote.http11.Http11NioProtocol,也就是NIO模式。通過之前的部落格分析,我們知道Connector元件在初始化和start的時候會觸發它子元件(Http11NioProtocol、NIOEndpoint的初始化和

曹工說Tomcat:200個http-nio-8080-exec執行全都被第三方服務拖住了,這可如何是好(上:執行模型解析)

# 前言 這兩年,tomcat慢慢在新專案裡不怎麼接觸了,因為都被spring boot之類的框架封裝進了內部,成了內建server,不用像過去那樣打個war包,再放到tomcat裡部署了。 但是,內部的機制我們還是有必要了解的,尤其是執行緒模型和classloader,這篇我們會聚焦執行緒模型。 其實

效能追擊:萬字長文30+圖揭祕8大主流伺服器程式執行模型 | Node.js,Apache,Nginx,Netty,Redis,Tomcat,MySQL,Zuul

> 本文為《[高效能網路程式設計遊記](https://www.itzhai.com/articles/decrypt-the-threading-model-of-common-server-programs.html)》的第六篇“效能追擊:萬字長文30+圖揭祕8大主流伺服器程式執行緒模型”。 ![ima

Java執行模型總結

1. 計算機系統 使用快取記憶體來作為記憶體與處理器之間的緩衝,將運算需要用到的資料複製到快取中,讓計算能快速進行;當運算結束後再從快取同步回記憶體之中,這樣處理器就無需等待緩慢的記憶體讀寫了。 快取一致性:多處理器系統中,因為共享同一主記憶體,當多個處理器的運算任務都設計到同一塊記憶體區域

Android執行模型--在子執行中更新UI

       Android是支援多執行緒的。主執行緒也稱UI執行緒,子執行緒也稱工作執行緒。一般耗時操作在子執行緒中進行,更新UI操作在主執行緒中進行。在主執行緒中執行耗時操作容易發生ANR錯誤,即應用程式無響應。而Android中又規定只有建立UI的執行緒

OSG 多執行模型 設計思想

A New Processing Model for Multithreaded, Multidisplay Scene Graphs Copyright © 2001 Don Burns (DB - Apr 28, 2004) This article

spring mvc tomcat 執行池的坑

1 配置tomcat  執行緒池設定為20個執行緒處理請求 2 後臺框架是springmvc   3 模擬10個請求 4  發現tomcat執行緒池沒一個幹活的 5 幹活的是spring自己建立的執行緒 為什麼springmvc

【轉】Leader-Follower執行模型

上圖就是L/F多執行緒模型的狀態變遷圖,共6個關鍵點: (1)執行緒有3種狀態:領導leading,處理processing,追隨following (2)假設共N個執行緒,其中只有1個leading執行緒(等待任務),x個processing執行緒(處理),餘下有N-1-x個following執行緒

執行、多程序之比較,以及三種執行模型

工作幾年找工作幾乎總會被問,從最開始的從網上看答案,到現在憑自己的經驗去說,這個問題似乎也是經驗積累的一個驗證,最近沒事就總結一下吧: 程序和執行緒的定義、比較等: 程序:處於活動狀態的計算機程式。程序就是在作業系統中       執行特定的任務,程序針對

pinpoint agent執行模型

pinpoint agent執行緒模型 以下分析基於pinpoint1.7.1版本 pinpoint agent主要使用到的非同步執行緒有4個 DeadlockMonitorThread : 死鎖監測執行緒,執行一次休眠60s public DeadlockMonitorThread(Deadlock

程序執行模型

文章目錄 程序的定義 程序控制塊PCB 程序狀態及狀態轉換 程序的三種基本狀態 三狀態模型及狀態轉換 程序的其它狀態 程序的五狀態模型 程序佇列 程序控制

Redis之單執行模型

Redis客戶端對服務端的每次呼叫都經歷了傳送命令,執行命令,返回結果三個過程。其中執行命令階段,由於Redis是單執行緒來處理命令的,所有每一條到達服務端的命令不會立刻執行,所有的命令都會進入一個佇列中,然後逐個被執行。並且多個客戶端傳送的命令的執行順序是不確定的。但是可以確定的是不會有兩條命

Netty(EventLoop 和執行模型)

EventLoop介面     Netty的EventLoop是協同設計的一部分,它採用了兩個基本的API:併發和網路程式設計。首先,io.netty.util.concurrent包構建在JDK的java.util.concurrent包上,用來提供執行緒執行