天天看點

Tomcat高效響應的秘密(一) Sendfile與Gzip

在通路網站的時候,你也許曾嘗試過或曾聽說過,為了提高性能,節省帶寬,我們把一些資源檔案進行壓縮,合并等,來實作上述目的。同時,應用伺服器為了節省帶寬,也會根據用戶端可以接受的壓縮類型進行資源的壓縮,這樣資源在回傳到用戶端時,再進行解壓渲染即可。

例如,我們請求時,請求頭可能是這個樣子:

Tomcat高效響應的秘密(一) Sendfile與Gzip

在Accept-Encoding中,注明了支援的壓縮類型。

對應的響應頭,我們會看到下面的内容:

Tomcat高效響應的秘密(一) Sendfile與Gzip

我們看到,伺服器通過<code>Content-Encoding</code>,注明伺服器使用<code>gzip</code>方式進行了壓縮。

請求響應相關的内容,前面寫到過:

Tomcat的Connector元件

Facade模式與請求處理

在Tomcat中,對于這種資源的壓縮也是支援的。配置項是Connector元件的<code>compression</code>屬性。

我們來看官方文檔的注釋

The Connector may use HTTP/1.1 GZIP compression in an attempt to save server bandwidth. The acceptable values for the parameter is “off” (disable compression), “on” (allow compression, which causes text data to be compressed), “force” (forces compression in all cases), or a numerical integer value (which is equivalent to “on”, but specifies the minimum amount of data before the output is compressed). If the content-length is not known and compression is set to “on” or more aggressive, the output will also be compressed. If not specified, this attribute is set to “off”.

如果不指定,預設是off狀态,支援off,on,force或者數字。

同時,在注釋下,還有一小段文字,注明除壓縮之外的另一個選擇,即sendfile功能。

Note: There is a tradeoff between using compression (saving your bandwidth) and using the sendfile feature (saving your CPU cycles).

對兩者而言,compression節省帶寬,而sendfile節省CPU。但有些Connector是不支援sendfile功能的。在支援該功能的Connector中,預設是開啟的。預設大于48Kb的靜态檔案,會直接使用sendfile功能進行傳送,而不再啟用壓縮。如果權衡兩者,要關閉sendfile功能,可以使用useSendfile屬性,或者在conf/web.xml中把預設的門檻值改大一些。

而sendfile之是以高效的原因,我們來看這裡

是基于NIO,使用了channel的transferTo方法,這個方法有什麼作用呢?

我們來看OpenJDK中,該方法的内容:

最終的高效處理,是通過NIO的MappedByteBuffer來實作的。就好比我們在運輸東西的時候,省去了解除安裝後重新裝車的過程,效率提高了不少。

我們前面寫到,compression預設是關閉的,而且開啟後,如果達到了sendfile的條件,也是優先使用sendfile,這一部分邏輯的控制如下,而且本身compression預設隻支援純文字,代碼中預置的mimeType隻有三種:

Tomcat高效響應的秘密(一) Sendfile與Gzip

這行代碼<code>if (entityBody &amp;&amp; (compressionLevel &gt; 0) &amp;&amp; !sendingWithSendfile)</code>判斷的項有很多,其中<code>compressionLevel</code>即compression的配置是屬于on/off/force這些,每個對應一個level,<code>sendingWithSendfile</code>則對應是否已使用sendfile,而<code>entityBody</code>則根據response的響應碼來進行判斷,如果是以下幾種情況,則也不會使用compression

判斷是否需要compression的邏輯,即未經壓縮,且超過設定大小的内容。

我們看,代碼中對于compression的處理,會添加響應頭<code>Content-Encoding</code>,同時在OutputBuffer中增加了一個GzipFilter,

那Gzip在輸出的時候,做的就是下面的工作:使用GZIPOutputStream進行輸出

總結起來,Tomcat為了将響應使用者的請求,同時又從節省帶寬和CPU的角度,提供了一些功能與配置。就sendfile這個功能來說,Nginx中同樣也提供類似的功能,實作原理我想應該也基本一緻。