在基於 HTTP 協議的網絡傳輸中 GZip 常常被使用,Nginx 中也可使用半行代碼開啓 GZip。GZip 壓縮的原理是什麼呢?本篇文章是我在網上閱讀了一些文檔後作的簡單總結。html
RFC 1952 是 GZIP file format specification version 4.3
。該規範主要定義了 GZip 壓縮的在數據格式方面的規範,以方便不一樣的操做系統、CPU、文件系統等之間進行文件傳輸交換。下面挑有意思的幾個點說,感興趣的能夠閱讀 RFC 1952 的原文。nginx
GZIP 的文件格式在設計上實際上是能夠容許一個文件裏有多個壓縮數據集(compressed data sets)—— GZIP 壓縮後的片斷拼接而成的。但就咱們大多數應用場景來講,基本上都是一個文件一個壓縮數據集,若是是多個文件一塊兒打包的話,也每每是將多個包合併成一個 tar 文件。git
每一個壓縮數據集都是下面的結構:github
| ID1 | ID2 | CM | FLG | MTIME(4字節) | XFL | OS | ---> moreweb
|
與|
之間是 1 byte,都是大端字節(Big Edian)算法
其中 ID1 和 ID2 分別是 0x1f 和 0x8b,用來標識文件格式是 gzip瀏覽器
CM 標識 加密算法,目前 0-7是保留字,8 指的是 deflate 算法bash
FLG 從低地址到高地址分別是 FTEXT、FHCRC、FEXTRA、FNAME、FCOMMENT、reserved、 reserved、reserved,這裏每一個 bit 被設置了以後有什麼意義感興趣的話能夠詳細參考 RFC 1952。比較有意思的是 FEXTRA,若是它被設置了表示存在額外的拓展字段。拓展字段的結構以下:服務器
<gzip@prep.ai.mit.edu>
發郵件申請。目前 Apollo file 就有本身的專屬 IDMTIME 指的是源文件最近一次修改時間,存的是 Unix 時間戳網絡
XFL 是給壓縮算法傳的一些參數,用來標識如何解壓。defalte 算法中 2 表示使用壓縮率最高的算法,4 表示使用壓縮速度最快的算法
OS 標識壓縮程序運行的文件系統,以處理 EOF 等的問題
more 後面是根據 FLG 的開啓狀況決定的,可能會有 循環冗餘校驗碼、源文件長度、附加信息等多種其餘信息
GZIP 的核心是 Deflate,在 RFC 1951 中被標準化,而且在當時做爲 LZW 的替代品有了很是普遍的使用。
Deflate 是一個同時使用 LZ77 與 Huffman Coding 的算法,這裏簡單介紹下這兩種算法的大體思路:
LZ77 的核心思路是若是一個串中有兩個重複的串,那麼只須要知道第一個串的內容和後面串相對於第一個串起始位置的距離 + 串的長度。
好比: ABCDEFGABCDEFH → ABCDEFG(7,6)H。7 指的是往前第 7 個數開始,6 指的是重複串的長度,ABCDEFG(7,6)H 徹底能夠表示前面的串,而且是沒有二義性的。
LZ77 用 滑動窗口(sliding-window compression)來實現這個算法。具體思路是掃描頭從串的頭部開始掃描串,在掃描頭的前面有一個長度爲 N 的滑動窗口。若是發現掃描頭處的串和窗口裏的 最長匹配串 是相同的,則用(兩個串之間的距離,串的長度)來代替後一個重複的串,同時還須要添加一個表示是真實串仍是替換後的「串」的字節在前面以方便解壓(此串須要在 真實串和替換「串」 以前都有存在)。
實際過程當中滑動窗口的大小是固定的,匹配的串也有最小長度限制,以方便 標識+兩個串之間的距離+串的長度 所佔用的字節是固定的 以及 不要約壓縮體積越大。更加詳細的實現能夠參考:Standford Edu. lz77 algorithm、 LZ77 Compression Algorithm、 LZ77壓縮算法編碼原理詳解(結合圖片和簡單代碼)
這裏經過這個壓縮機制也就能比較容易的解釋爲啥 CSS BEM 寫法 GZIP 壓縮以後能夠忽略長度以及 JPEG 圖片 GZIP 以後可能會變大 的狀況了
解壓:GZIP 的壓縮由於要在窗口裏尋找重複串相對來講效率是比較低的(LZ77 仍是經過 Hash 等系列方法提升了不少),那解壓又是怎麼個狀況呢?觀察壓縮後的整個串,每一個小串前都有一個標識要標記是原始串仍是替換「串」,經過這個標識就能以 O(1)的複雜度直接讀完而且替換完替換「串」,總體上效率是很是可觀的。
Huffman Coding 是大學課本中通常都會提到的算法。核心思路是經過構造 Huffman Tree 的方式給字符從新編碼(核心是避免一個葉子的路徑是另一個葉子路徑的前綴),以保證出現頻路越高的字符佔用的字節越少。關於 Huffman Tree 的構造這裏再也不細說,不太清楚的能夠參考:Huffman Coding。
解壓:Huffman Coding 以後須要維護一張 Huffman Map 表,來記錄從新編碼後的字符串,根據這張表,還原原始串也是很是高效的。
Deflate 綜合使用了 LZ77 和 Huffman Coding 來壓縮文件,相對而言又提高了不少。詳細能夠參考 gzip原理與實現
在 RFC 2016 中 GZIP 已經成爲了規定的三種標準HTTP壓縮格式之一。目前絕大多數的網站都在使用 GZIP 傳輸 HTML、CSS、JavaScript 等資源文件。
Nginx 的 ngx_http_gzip_module 也提供了開啓 GZIP 壓縮的方式,有下面的一些經常使用配置:
# 開啓
gzip on;
# 壓縮等級,1-9。設置多少能夠參考:http://serverfault.com/questions/253074/what-is-the-best-nginx-compression-gzip-level
gzip_comp_level 2;
# "MSIE [1-6]\." 好比禁止 IE6 使用 GZIP
gzip_disable regex ...
# 最小壓縮文件長度
gzip_min_length 20;
# 使用 GZIP 壓縮的最小 HTTP 版本
gzip_http_version 1.1;
# 壓縮的文件類型,值是 [MIME type](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types)
gzip_types text/html;
複製代碼
Nginx 上開啓 GZIP 以後,理論上會按照 GZIP 配置打開壓縮。那如何檢測是否開啓成功了呢?
打開瀏覽器,訪問你的網站,看 Chrome 的 Network,若是 Size 上有兩個不同大小的體積(如:222KB 和 613KB),則表明 GZIP 已經成功開啓。
那瀏覽器又是如何和服務器配合的呢?
瀏覽器在請求資源的時候再 header 裏面帶上 accept-encoding: gzip
的參數。Nginx 在接收到 Header 以後,發現若是有這個配置,則發送 GZIP 以後的文件(返回的 header 裏也包含相關的說明),若是沒有則發送源文件。瀏覽器根據 response header 來處理要不要針對返回的文件進行解壓縮而後展現。