關於Gzip壓縮

一、Gzip

gzip是GNUzip的縮寫,最早用於UNIX系統的文件壓縮。HTTP協議上的gzip編碼是一種用來改進web應用程序性能的技術,web服務器和客戶端(瀏覽器)必須共同支持gzip。目前主流的瀏覽器,Chrome,firefox,IE等都支持該協議。常見的服務器如Apache,Nginx,IIS同樣支持gzip。

gzip壓縮比率在3到10倍左右,可以大大節省服務器的網絡帶寬。而在實際應用中,並不是對所有文件進行壓縮,通常只是壓縮靜態文件。

那麼客戶端和服務器之間是如何通信來支持gzip的呢?通過圖1我們可以很清晰的瞭解
在這裏插入圖片描述
圖1 gzip工作原理圖

1)瀏覽器請求url,並在request header中設置屬性accept-encoding:gzip。表明瀏覽器支持gzip。

2)服務器收到瀏覽器發送的請求之後,判斷瀏覽器是否支持gzip,如果支持gzip,則向瀏覽器傳送壓縮過的內容,不支持則向瀏覽器發送未經壓縮的內容。一般情況下,瀏覽器和服務器都支持gzip,response headers返回包含content-encoding:gzip。

3)瀏覽器接收到服務器的響應之後判斷內容是否被壓縮,如果被壓縮則解壓縮顯示頁面內容。

下面以淘寶爲例,驗證一下開啓gzip的效果。客戶端(瀏覽器)請求http://www.taobao.com/。本次測試使用的瀏覽器爲Chrome,打開控制檯查看網絡信息可以看到request headers中包含:accept-encoding:gzip, deflate, sdch,表明chrome瀏覽器支持這三種壓縮。這裏值得一提的是accept-encoding中添加的另外兩個壓縮方式deflate和sdch。deflate與gzip使用的壓縮算法幾乎相同,這裏不再贅敘。sdch是Shared Dictionary Compression over HTTP的縮寫,即通過字典壓縮算法對各個頁面中相同的內容進行壓縮,減少相同的內容的傳輸。sdch是Google推出的,目前只在Google Chrome, Chromium 和Android中支持。圖2爲瀏覽器發送的request header。圖3爲服務器返回的response header。
在這裏插入圖片描述
圖2 淘寶request header
在這裏插入圖片描述
圖3 淘寶response header

通過圖2以圖3很明顯可以看出網站支持gzip,那麼當支持gzip之後,壓縮效率如何體現呢?通常瀏覽器都有現成的插件檢測gzip壓縮效率,如firefoxd的YSlow插件。
還有一些測試工具,自己找一下

二、nginx啓用gzip

Nginx的壓縮輸出有一組gzip壓縮指令來實現。相關指令位於http{….}兩個大括號之間,如下:

#打開gzip壓縮
  gzip on;
  
  #不壓縮臨界值,大於1K的才壓縮,一般不用改
  gzip_min_length 1k;
  
  #設置系統獲取幾個單位的緩存用於存儲gzip的壓縮結果數據流,這裏設置以16k爲單位的4倍申請內存
  gzip_buffers 4 16k;
  
  #默認爲http 1.1,現在99.99%的瀏覽器基本上都支持gzip解壓了,所有無需設置此項
  #gzip_http_version 1.0;
  
  #gzip壓縮比,1 最小處理速度最快,9 最大但處理最慢(傳輸快但比較消耗cpu)
  gzip_comp_level 2;
  
  #要壓縮的文件類型,注意"text/html"類型無論是否指定總是會被壓縮的
  gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript application/x-httpd-php image/jpeg image/gif image/png;
  
  #on的話會在Header裏增加"Vary: Accept-Encoding",給代理服務器用的,有的瀏覽器支持壓縮,有的不支持,所以避免浪費不支持的也壓縮,所以根據客戶端的HTTP頭來判斷,是否需要壓縮
  #我這裏的瀏覽器肯定支持gzip壓縮,所以就不開啓此功能了
  gzip_vary off;
  
  #IE6對Gzip不怎麼友好,不給它Gzip壓縮了
  gzip_disable "MSIE [1-6]\.";

三、tomcat啓用gzip

目前大多數主流WEB中間件都支持GZIP壓縮、下面以Tomcat 爲例進行說明:
找到Tomcat 目錄下的conf下的server.xml,並找到如下信息:

<Connector port = "8080" maxHttpHeaderSize = "8192" maxThreads = "150" minSpareThreads = "25"
              maxSpareThreads = "75" enableLookups = "false" redirectPort = "8443" acceptCount = "100"
              connectionTimeout = "20000" disableUploadTimeout = "true"
      將它改成如下的形式(其實在上面代碼的下面已經有了,將他們打開而已。):
      <Connector port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25"
             maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100"
             connectionTimeout="20000" disableUploadTimeout="true"
             compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata"
             compressableMimeType="text/html,text/xml" >
      這樣,就能夠對html和xml進行壓縮了,如果要壓縮css 和 js,那麼需要將
           compressableMimeType=”text/html,text/xml”加入css和js:
           <Connector port="8080" ......... compressableMimeType="text/html,text/xml,text/css,text/javascript" >

四、java服務器啓用gzip

java本身可以通過過濾器filter實現gzip壓縮。下面提供一個gzip工具類:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

public class GZIPUtils  {
    public static final String GZIP_ENCODE_UTF_8 = "UTF-8"; 
    public static final String GZIP_ENCODE_ISO_8859_1 = "ISO-8859-1";


    public static byte[] compress(String str, String encoding) {
        if (str == null || str.length() == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        GZIPOutputStream gzip;
        try {
            gzip = new GZIPOutputStream(out);
            gzip.write(str.getBytes(encoding));
            gzip.close();
        } catch ( Exception e) {
            e.printStackTrace();
        }
        return out.toByteArray();
    }

    public static byte[] compress(String str) throws IOException {  
        return compress(str, GZIP_ENCODE_UTF_8);  
    }

    public static byte[] uncompress(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);
            byte[] buffer = new byte[256];
            int n;
            while ((n = ungzip.read(buffer)) >= 0) {
                out.write(buffer, 0, n);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return out.toByteArray();
    }

    public static String uncompressToString(byte[] bytes, String encoding) {  
        if (bytes == null || bytes.length == 0) {  
            return null;  
        }  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        ByteArrayInputStream in = new ByteArrayInputStream(bytes);  
        try {
            GZIPInputStream ungzip = new GZIPInputStream(in);  
            byte[] buffer = new byte[256];  
            int n;  
            while ((n = ungzip.read(buffer)) >= 0) {  
                out.write(buffer, 0, n);  
            }  
            return out.toString(encoding);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String uncompressToString(byte[] bytes) {  
        return uncompressToString(bytes, GZIP_ENCODE_UTF_8);  
    } 

    public static void main(String[] args) throws IOException {
        String s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        System.out.println("字符串長度:"+s.length());
        System.out.println("壓縮後::"+compress(s).length);
        System.out.println("解壓後:"+uncompress(compress(s)).length);
        System.out.println("解壓字符串後::"+uncompressToString(compress(s)).length());
    }
}

這裏值得一提的是gzip的壓縮率,並不是經過gzip壓縮之後,所有文件都會變小。根據gzip使用的算法特性,代碼相似率越大壓縮效率越高。所以對於靜態資源量非常大的網站,開啓gzip可節省大量流量,而同時gzip的應用遠不止提高web性能,Android,IOS底層網絡請求同樣可用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章