Nginx的Gzip功能

什麼是HTTP壓縮

有時候客戶端和伺服器之間會傳輸比較大的報文數據,這時候就佔用較大的網路頻寬和時長。為了節省頻寬,加速報文的響應速速,可以將傳輸的報文數據先進行壓縮,然後再進行傳輸。
HTTP支援多種報文壓縮演算法,下面是一個普通的請求頭,從Accept-Encoding欄位可以看出支援gzip、deflate和br壓縮演算法。本文我們重點講使用Gzip演算法對報文進行壓縮,比如Gzip來壓縮HTML,Javascript, CSS文件,壓縮完後能大大減少網路傳輸的數據量,提高了用戶顯示網頁的速度。

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Host: localhost:8000
If-Modified-Since: Tue, 21 Apr 2020 14:09:01 GMT
If-None-Match: "5e9efe7d-264"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36

任何技術都是雙面的,HTTP壓縮雖然能減少頻寬佔用加快響應速度,但是因為需要額外的壓縮和解壓的過程,所以會佔用些客戶端或伺服器端的計算資源。

了解HTTP的讀者知道,可以對HTTP的報文體進行編碼加密。其實HTTP壓縮是一種特殊的編碼方式,使用這種編碼方式可以將報文大大減少,使用對應的解法方式又能還原最初的報文。(我們可以看出,其實壓縮技術的本質就是一種編碼方式)

HTTP壓縮的使用場景

從上面對於HTTP壓縮的介紹可以看出,這種技術是一種優化技術,常常用於壓縮伺服器端返回的報文以達到節省頻寬加速響應的目的。

下面簡單介紹一個HTTP使用Gzip壓縮的過程。

  • 瀏覽器發送Http request 給Web伺服器, request 中有Accept-Encoding: gzip, deflate, br。 (告訴伺服器,瀏覽器支援gzip壓縮)

  • Web伺服器接到request後, 先生成原始的Response, 其中有原始的Content-Type和Content-Length。

  • Web伺服器通過Gzip,來對Response進行編碼, 編碼後header中有Content-Type和Content-Length(壓縮後的大小), 並且增加了Content-Encoding:gzip. 然後把Response發送給瀏覽器。

  • 瀏覽器接到Response後,根據Content-Encoding:gzip來對Response進行解碼。 獲取到原始response後, 然後顯示出網頁。

客戶端也可以發送壓縮數據給服務端,通過程式碼將請求數據解壓即可,規範起見同樣要在請求中加入Content-Encoding:gzip

用Nginx實現HTTP壓縮

Nginx提供了對HTTP Gzip壓縮的支援,這邊我們就來看看適應Nginx怎麼對返回報文進行壓縮。

Nginx中通過ngx_http_gzip_module模組、ngx_http_gzip_static_module模組和ngx_http_gunzip_module模組來對Gzip功能進行支援。一般情況下Nginx默認會編譯
這些模組,可以通過nginx -V命令看下你安裝的nginx是否包含了這些模組。

Gzip相關的指令可以在配置文件的http塊、server塊或者location塊中

ngx_http_gzip_module模組

ngx_http_gzip_module模組主要負責Gzip功能的開啟和設置,對響應數據進行在線實時壓縮。該模組包含以下主要指令。

# 開啟或者關閉Gzip功能,默認情況下,該指令設置為off,即不啟用Gzip功能。只有將該指令設置為on時,其他指令設置才有效
gzip on | off

# 設置Gzip壓縮文件使用快取空間的大小
# 默認值是:gzip_buffers 32 4k|16 8k
gzip_buffers number size;

# 該指令用於設定Gzip壓縮程度,包括級別1到級別9。
# 級別1表示壓縮程度最低,壓縮效率最高;級別9表示壓縮程度最高,壓縮效率最低,最費時間。
# 默認是1
gzip_comp_level level

# 針對不同種類客戶端發起的請求,可以選擇性地開啟和關閉Gzip功能。
# 支援正則表達式,其中,regex 根據客戶端的瀏覽器標誌(User-Agent,UA)進行設置。
gzip_disable regex ...;

# 該設置使用了正則表達式,其可以匹配UC字元串中包含MSIE 4、MSIE 5和MSIE6的所有瀏覽器。
# 響應這些瀏覽器發出的請求時,Nginx伺服器不進行Gzip壓縮。
gzip_disable MSIE [4-6]\.;

# 早期的一些瀏覽器或者HTTP客戶端,可能不支援Gzip自解壓,因此用戶有時會看到亂碼,所以針
# 對不同的HTTP協議版本,需要選擇性地開啟或者關閉Gzip功能。該指令用於設置開啟Gzip功能的最低HTTP協議版本。
# 默認設置為1.1版本,即只有客戶端使用1.1及以上版本的HTTP協議時,才使用Gzip功能對響應輸出數據進行壓縮。
# 從目前來看,絕大多數的瀏覽器都支援Gzip自解壓,一般採用默認值即可.
zip_http_version 1.0 | 1.1;

# 該指令設置頁面的位元組數,當響應頁面的大小大於該值時,才啟用Gzip功能。
# 建議設置成gzip_min_length 1024;
gzip_min_length length;

# 用於設置Nginx伺服器是否對後端伺服器返回的結果進行Gzip壓縮;
# 一般情況下,後端都是用來做restAPI介面,返回的數據量不會太大,不建議進行壓縮
# 真的需要對後端返回的數據進行壓縮是可以再看下這塊的內容
gzip_proxied off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any ...;

# 設置MIME類型,被設置的類型將被壓縮,默認值是:text/html
# 該變數還可以取「*」,表示對所有MIME類型的頁面數據進行Gzip壓縮
# 一般可以設置成:gzip_types text/plain  application/javascript  text/css text/xml
gzip_types mime-type ...;

# 開啟後的效果是在響應頭部添加了Accept-Encoding: gzip
gzip_vary on | off;

ngx_http_gzip_static_module模組

ngx_http_gzip_static_module模組主要負責搜索和發送經過Gzip功能預壓縮的數據。這些數據以「.gz」作為後綴名存儲在伺服器上。如果客戶端請求的數據在之前被壓縮過,並且客戶端瀏覽器支援Gzip壓縮,就直接返回壓縮後的數據。

該模組與ngx_http_gzip_module模組的不同之處主要在於,該模組使用的是靜態壓縮,在HTTP響應頭部包含Content-Length頭域來指明報文體的長度,用於伺服器可確定響應數據長度的情況;而後者默認使用Chunked編碼的動態壓縮,其主要適用於伺服器無法確定響應數據長度的情況,比如大文件下載的情形,這時需要實時生成數據長度。

該模組指令的使用和ngx_http_gzip_static_module模組類似,這邊就不再具體展開了。大家可以參考官方文檔

該模組是Nginx伺服器的可選HTTP模組,如果要使用,必須在Nginx程式配置時添加–with-http_gzip_static_module指令。

ngx_http_gunzip_module模組

Nginx伺服器支援對響應輸出數據流進行Gzip壓縮,這對客戶端瀏覽器來說,需要有能力解壓和處理Gzip壓縮數據,但如果客戶端本身不支援該功能,就需要Nginx伺服器在向其發送數據之前先將該數據解壓。這些壓縮數據可能來自於後端伺服器壓縮產生或者Nginx伺服器預壓縮產生。ngx_http_gunzip_module模組便是用來針對不支援Gzip壓縮數據處理的客戶端瀏覽器,對壓縮數據進行解壓處理的.

同樣,對這個模組的指令使用就不具體展開了,大家可以參考官方文檔

現代的瀏覽器一般都支援壓縮功能,所以這個模組使用到的幾率較小。

配置列子

gzip            on;
gzip_min_length 1000;
gzip_proxied    expired no-cache no-store private auth;
gzip_types      text/plain application/xml;

為了使得Nginx伺服器能夠在全局範圍內應用Gzip壓縮功能,可以將Gzip配置放在了http全局塊中。如果要對各個虛擬主機差別性對待,我們可以在對應的server
塊中添加各自的Gzip配置指令;

閱讀延伸

平時開發的應用中可能不是所有的應用都使用了Nginx,看看其他Web伺服器怎麼開啟對HTTP壓縮的支援。

1. Spring Boot中內嵌的Tomcat開啟壓縮功能

Tomcat作為servet容器+http server,也是支援gzip壓縮的。使用傳統的Tomcat的話,我們只需要在server.xml配置開啟HTTP壓縮即可。
在embed版本下需要通過程式碼來配置。spring-boot內置的tomcat是embed版本,通過內置的autoconfig機制已經做了一些默認tomcat配置,但是對於一些不常用/高級的配置,spring-boot並沒有提供入口。

不過由於spring bean的特性,可以覆蓋默認裝配的bean,包括tomcat相關的配置。使用TomcatConnectorCustomizer介面可以開啟壓縮配置。

public class ConnC1 implements TomcatConnectorCustomizer{

    @Override
    public void customize(Connector connector) {
        ProtocolHandler protocolHandler = connector.getProtocolHandler();
        if(protocolHandler instanceof Http11NioProtocol){
            Http11NioProtocol http11NioProtocol = (Http11NioProtocol)protocolHandler;
            http11NioProtocol.setCompression("on");//default off
            http11NioProtocol.setCompressibleMimeType();
            http11NioProtocol.setCompressionMinSize(2048);//default 2048(B)
            http11NioProtocol.setMaxKeepAliveRequests(1);//default 200
        }
    }
}

關於Tomcat對於HTTP壓縮的支援,大家可以從Tomcat的CompressionConfig這個類開始尋找線索。

其實如果只是簡單開啟對壓縮功能的支援的話,只要在Spring Boot做下面的配置即可:

server:
  compression:
    enabled: true
    min-response-size: 1024
    mime-types:
       application/json

問題

歡迎大家留言說說Gzip功能還有哪些常用場景~