高性能Tomcat:漫談行走在sendfile之上的Tomcat

jopen 9年前發布 | 14K 次閱讀 Tomcat 應用服務器

原文出處: 陸晨

Tomcat常用的三種IO模式

提起高性能JAVA IO大家一定會想到NIO,AIO等等,然而我們的Tomcat是java寫的一個優秀的開源的web服務,它與NIO有什么關系呢?我們的tomcat應用是不是早就跑在nio之上了呢?

答案是Tomcat是支持NIO的,到Tomcat7為止,默認的Tomcat包里面的配置并沒有開啟NIO連接器。下面我先簡單介紹一下Tomcat的三種IO策略:BIO,NIO,APR。

BIO:大家是否記得我們初學網絡編程的時候,我們寫的第一個服務器程序……,BIO是Tomcat默認開啟的IO模式,性能灰常底下,沒有經過任何的優化處理。

NIO:利用JAVA的非阻塞IO技術,開啟這個模式非常簡單,只需要把tomcat子目錄conf下面的server.xml修改一下即可,找到下面,將protocol改為org.apache.coyote.http11.Http11NioProtocol即可。

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
上面是默認的配置,我們改成下面這個樣子:

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

這樣重啟你的tomcat就好,有沒有被easy到?

APR:這種模式使用起來最優門檻,除非要將上面的配置改成下面這樣:

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

還需要下載APR模式需要的庫,因為這種模式是基于JNI的,即JAVA調用本地庫的方式來進行IO。

Tomcat對靜態資源的處理

大家都知道,Tomcat為我們提供了一個專門用來處理靜態資源的Servler,它叫DefaultServlet,看看這個Servlet的源代碼,其中對資源處理方法的實現里面有下面這樣一段:

if (ostream != null) {
                    if (!checkSendfile(request, response, cacheEntry, contentLength, null))
                        copy(cacheEntry, renderResult, ostream);
                } else {
                    copy(cacheEntry, renderResult, writer);
                }

作者的意思是這里判斷如果沒有滿足使用sendfile條件的情況下我們就直接將靜態資源數據讀取,寫入response的輸出流,那么如果滿足使用sendfile條件,這里是不做處理的,這里會把資源文件的大小即名稱放在request的屬性集合里面帶給底層,讓底層來處理。

Tomcat定義了三種類型的Endpoint,他們分別是BIO,NIO及APR三種模式下底層網絡處理的具體實現,比如NioEndpoint里面調用了FileChannel的transferTo方法,transferTo會利用操作系統的sendfile系統調用來將磁盤文件輸出到網絡。同樣AprEndpoint最為哦APR方式的實現會調用Native來實現IO。

send和sendfile

Tomcat中的NIO模式和APR模式對靜態文件處理的高效根源在于地從對不同的系統調用,和普通的文件傳輸不同的是NIO和APR方式都使用的操作系統的sendfile系統調用來對文件進行IO,而普通的方式是使用了read和write系統調用。name這兩種類型有什么不同呢,這是本文的關鍵點:

 高性能Tomcat:漫談行走在sendfile之上的Tomcat

文件讀取以發送至網絡流程圖

先看看上面這張圖,使用read和write方式的時候,將文件輸出到網絡的流程是這樣的:

1,read操作先將線程從用戶態切換到內核態,將文件從磁盤讀到內核緩沖器。

2,read將文件從內核緩沖區讀到用戶地址空間,同時線程從內核態切換到用戶態。

3,read返回。

4,對文件進行處理

5,write將線程從用戶態切換到內核態,將文件寫到操作系統內核網絡部分的緩沖區。

6,write將線程切換到用戶態并返回。

上面的的過程涉及到四次操作系統內核態與用戶態的切換,代價是昂貴的。由于事實上我們并不需要對靜態文件進行處理這個步驟,為什么要繞一個圈子呢從內核copy到用戶態又copy到內核態呢,因此sendfile來了。

我們再來看看sendfile的處理流程:

1,將文件讀到操作系統的內核緩沖區。

2,將文件copy到操作系統跟網絡相關的內核緩沖區。

上面不會涉及到內核態到用戶態以及用戶態到內核態的切換,sendfile是linux2+version提供的系統調用,而且在linux2.4+version版本之后提供能zero-copy特性,上面這些說明了sendfile為我們的程序提高了問靜態文件處理的能力和性能。

</div>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!