天天看點

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

本文主要包括Tomcat9的NIO、NIO2、APR三種I/O模型的工作原理以及使用Jmeter對其進行持續壓力測試。

1、connector的工作原理

這裡我們說的Tomcat中三種不同的I/O模型主要指的是其連接配接器(connector)的工作模型,對于tomcat而言,連接配接器一般指的是coyote,其工作原理大緻如下圖所示:

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

連接配接器中的各個元件的作用如下:

1.1

EndPoint

EndPoint

即Coyote通信端點,是通信監聽的接口,是具體Socket接收和發送處理器,是對傳輸層(四層)的抽象,是以

EndPoint

用來實作TCP/IP協定的。Tomcat 并沒有

EndPoint

接口,而是提供了一個抽象類

AbstractEndpoint

, 裡面定義了兩個内部類:

Acceptor

SocketProcessor

Acceptor

用于監聽Socket連接配接請求。

SocketProcessor

用于處理接收到的Socket請求,它實作

Runnable

接口,在

Run

方法裡 調用協定處理元件

Processor

進行處理。為了提高處理能力,

SocketProcessor

被送出到線程池來執行,而這個線程池叫作執行器(Executor)。

1.2

Processor

**

Processor

是coyote的協定處理接口 。**如果說EndPoint是用來實作TCP/IP協定的,那麼

Processor

用來實作HTTP協定,

Processor

接收來自EndPoint的Socket,讀取位元組流解析成Tomcat的

Request

Response

對象,并通過

Adapter

将其送出到容器處理,

Processor

是對應用層(七層)協定的抽象。

1.3

ProtocolHandler

**

ProtocolHandler

是Coyote的協定接口,通過Endpoint和Processor ,實作對具體協定(HTTP或AJP)的處理。**Tomcat 按照協定和I/O 提供了6個實作類 :

AjpNioProtocol

AjpAprProtocol

AjpNio2Protocol

Http11NioProtocol

Http11Nio2Protocol

Http11AprProtocol

。我們在配置

tomcat/conf/server.xml

中的

connecter

塊時 , 至少要指定具體的

ProtocolHandler

, 當然也可以指定協定名稱(如HTTP/1.1)。

1.4

Adapter

由于協定不同,用戶端發過來的請求資訊也不盡相同,Tomcat定義了自己的Request類來存放這些請求資訊。

ProtocolHandler

接口負責解析請求并生成Tomcat的

Request

類。 但是這個Request對象不是标準的ServletRequest,不能用來作為參數來調用容器。是以需要引入

CoyoteAdapter

,連接配接器調用

CoyoteAdapter

Sevice

方法,傳入Tomcat的

Request

對象,CoyoteAdapter将

Request

轉成

ServletRequest

,再調用容器的Service方法。

2、三種I/O模型原理

在開始之前,我們先看一下tomcat官網給出的這三種I/O模型的工作參數的一個對比圖:

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

這裡我們可以看到一般說的NIO、NIO2和APR使用的是非阻塞方式指的就是在讀取請求報頭和等待下一個請求的時候是使用的非阻塞方式。

Tomcat的NIO是基于I/O複用(同步I/O)來實作的,而NIO2是使用的異步I/O。參考經典書籍《UNIX網絡程式設計 卷1 套接字聯網API》,兩者的主要原理如下:

I/O複用(NIO)

I/O複用(I/O multiplexing)可以調用

select

poll

,阻塞在這兩個系統調用中的某一個之上,而不是阻塞在真正的I/O系統調用上。程序阻塞于

select

調用,等待資料報套接字變為可讀。當

select

傳回套接字可讀這一條件時,程序調用

recvfrom

把所讀資料報複制到應用程序緩沖區,盡管這裡需要使用

select

recvfrom

兩個系統調用,但是使用

select

的可以等待多個描述符就緒,即可以等待多個請求。

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

異步IO(NIO2)

異步I/O(asynchronous I/O)的工作機制是:告知核心啟動某個操作,并讓核心在整個操作(包括将資料從核心複制到應用程式的緩沖區)完成後通知應用程式。需要注意的是:異步I/O模型是由核心通知應用程序I/O操作何時完成。

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

最後我們可以把上面的過程結合剩下沒有提到的三種UNIX系統中的IO模型進行對比得到下圖:

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

NIO、NIO2和APR的差別

NIO NIO2 APR
實作 JAVA NIO庫 JDK1.7 NIO2庫 C
IO模型 同步非阻塞 異步非阻塞 取決于系統
APR的重點在于使用C語言實作并且能夠跨平台使用,它相當于将UNIX系統中的IO操作進行了一層封裝使得程式設計開發更容易

3、connector的幾個重要參數

connectionTimeout

The number of milliseconds this Connector will wait, after accepting a connection, for the request URI line to be presented. Use a value of -1 to indicate no (i.e. infinite) timeout. The default value is 60000 (i.e. 60 seconds) but note that the standard server.xml that ships with Tomcat sets this to 20000 (i.e. 20 seconds). Unless disableUploadTimeout is set to

false

, this timeout will also be used when reading the request body (if any).

在connector和請求的用戶端建立連接配接之後開始計時,當超過該值的時候就會逾時,然後斷開連接配接。使用值-1表示無逾時,預設值為60000(即60秒),但Tomcat中的server.xml将此值設定為20000(即20秒)。

除非disableUploadTimeout設定為false,否則在讀取請求正文(如果有)時也會使用此逾時。

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. Note that if an executor is configured any value set for this attribute will be recorded correctly but it will be reported (e.g. via JMX) as

-1

to make clear that it is not used.

最大線程數,大并發請求時,tomcat能建立來處理請求的最大線程數,超過則放入請求隊列中進行排隊,預設值為200。

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.

當最大線程數(maxThreads)被使用完時,可以放入請求隊列排隊個數,超過這個數傳回connection refused(請求被拒絕),預設值為100;

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 is

8192

.For NIO/NIO2 only, setting the value to -1, will disable the maxConnections feature and connections will not be counted.

Tomcat在任意時刻接收和處理的最大連接配接數。當Tomcat接收的連接配接數達到maxConnections時,Acceptor線程不會讀取accept隊列中的連接配接;這時accept隊列中的線程會一直阻塞着,直到Tomcat接收的連接配接數小于maxConnections。預設值為8192。

對于NIO / NIO2,将該值設定為-1将禁用maxConnections功能,并且不計算連接配接數。

圖解

按照被處理的先後順序我們可以把tomcat中的線程隊列和以上四個參數使用該圖進行表示

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果
  • maxThreads + acceptCount < maxConnections

    的時候将不會有線程被阻塞
  • 當阻塞的線程時間超過connectionTimeout還沒得到傳回值将傳回連接配接逾時

4、配置測試環境

4.1 配置connector

首先我們需要在tomcat中配置三個connector,分别對應三種I/O模型:

<Connector port="8080" protocol="org.apache.coyote.http11.Http11AprProtocol"
               connectionTimeout="20000"
               redirectPort="8443"
               acceptCount="20000"
               maxThreads="16"
               maxConnections="22000"/>
    <Connector port="8081" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
               connectionTimeout="20000"
               redirectPort="8444"
               acceptCount="20000"
               maxThreads="200"
               maxConnections="22000"/>
    <Connector port="8082" protocol="org.apache.coyote.http11.Http11NioProtocol"
               connectionTimeout="20000"
               redirectPort="8445"
               acceptCount="20000"
               maxThreads="16"
               maxConnections="22000"/>
           

4.2 配置jmeter

4.2.1 測試環境

jmeter是apache旗下的一款開源的使用JAVA編寫的伺服器壓力測試軟體,我們從官網下載下傳源碼包,分别部署在windows和Linux系統上,因為windows系統的硬體配置太差了,沒辦法進行高并發的壓力測試,是以windows平台隻進行jmeter的測試檔案jmx的配置,配置完成後再使用Linux測試機來進行壓力測試。(注意jmeter版本需要保持一緻)

使用jmeter進行測試的機器系統和核心版本為:

[[email protected] ~]# lsb_release -a
LSB Version:    :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch
Distributor ID: RedHatEnterpriseServer
Description:    Red Hat Enterprise Linux Server release 6.9 (Santiago)
Release:        6.9
Codename:       Santiago
[[email protected] ~]# uname -r
2.6.32-696.el6.x86_64
           

安裝tomcat9的伺服器系統和核心版本為:

[[email protected] conf]# lsb_release -a
LSB Version:    :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch
Distributor ID: n/a
Description:    NAME="Red Hat Enterprise Linux Server"
Release:        n/a
Codename:       n/a
[[email protected] conf]# uname -r
3.10.0-1062.18.1.el7.x86_64
           

4.2.2 配置jmeter

jmeter使用前需要配置JDK和系統環境變量(JDK配置這裡不再贅述),我們在

/etc/profile

中導入相關變量并使用source指令保證生效。

export JMETER_HOME=/home/jmeter
export CLASSPATH=$JMETER_HOME/lib/ext/ApacheJMeter_core.jar:$JMETER_HOME/lib/jorphan.jar:$CLASSPATH
export PATH=$JMETER_HOME/bin:$PATH
           

配置成功後應該可以看到如下輸出

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

4.3 編輯JMX檔案

JMX的檔案配置不算複雜,最重要的是測試的時間和并發線程數量

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

這裡我們使用持續壓力測試模式,設定循環次數為永遠,然後設定持續時間為300秒即5分鐘,設定線程數為200并且ramp-up時間為1s即每秒200并發數,如果ramp-up時間為10s即每秒200÷10=20并發數,以此類推。對應到jmx檔案中的xml檔案塊為:

<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="線程組" enabled="true">
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循環控制器" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <intProp name="LoopController.loops">-1</intProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">200</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <boolProp name="ThreadGroup.scheduler">true</boolProp>
        <stringProp name="ThreadGroup.duration">300</stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
      </ThreadGroup>
           

4.4 測試類型

這裡我們分别測試五分鐘持續壓測情況下200、400、600、800、1000的并發情況,測試的頁面為tomcat的預設首頁,tomcat自帶的

examples

中的

/examples/servlets/nonblocking/bytecounter.html

/examples/servlets/nonblocking/numberwriter

。可以看到後面的兩個example都是使用非阻塞的方式進行編寫的

sevlet

。三者的主要操作如下:

  • tomcat首頁幾乎相當于一個靜态頁面,屬于簡單的網頁請求操作,應用程式發送請求到核心,核心從IO從讀取相應檔案并傳回;
  • numberwriter

    是生成傳回一串很長的數字,應用程式發送請求到核心并接收從核心生成傳回的較大的資料;
  • bytecounter

    需要上傳一個檔案然後再計算字數(這裡使用了一個大小約30KB的markdown檔案作為測試),需要進行IO傳輸和CPU計算再從核心傳回一個簡單的數值到應用程式;

4.5 tomcat9啟動參數

此處我們使用的依舊是systemd調用jsvc啟動tomcat,啟動參數如下:

ExecStart=/home/tomcat9/bin/jsvc \
        -user tomcat \
        -nodetach \
        -java-home ${JAVA_HOME} \
        -Xms4096m \
        -Xmx8192m \
        -XX:NewRatio=3 \
        -XX:SurvivorRatio=4 \
        -pidfile ${CATALINA_BASE}/tomcat.pid \
        -classpath ${CATALINA_HOME}/bin/bootstrap.jar:${CATALINA_HOME}/bin/tomcat-juli.jar \
        -outfile ${CATALINA_BASE}/logs/catalina.out \
        -errfile ${CATALINA_BASE}/logs/catalina.err \
        -Dcatalina.home=${CATALINA_HOME} \
        -Dcatalina.base=${CATALINA_BASE} \
        -Djava.io.tmpdir=${CATALINA_TMPDIR} \
        -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager \
        -Djava.util.logging.config.file=${CATALINA_BASE}/conf/logging.properties \
        -Djava.library.path=/usr/local/apr/lib \
        org.apache.catalina.startup.Bootstrap
           

5、測試結果

5.1 tomcat首頁測試結果

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果
Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

對于簡單的請求,三種模式的所有表現資料都幾乎一樣,基本不存在測試誤差範圍外的差距。

5.2 numberwriter測試結果

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果
Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

到了numberwrite這一種傳回較長資料的請求,NIO2模型的錯誤率要比其他兩者低得多,到了1200并發的時候apr模型和NIO模型的錯誤率都已經超過了六成,個人認為此時的響應時間不具有參考性。

5.3 wordcount測試結果

Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果
Tomcat篇03-使用Jmeter對Tomcat9的三種IO模型進行持續壓力測試1、connector的工作原理2、三種I/O模型原理3、connector的幾個重要參數4、配置測試環境5、測試結果

和之前的numberwrite一樣,同樣是犧牲了響應時間而降低了錯誤率。