優雅地實現 TCP 壓縮傳輸

zxc321 8年前發布 | 10K 次閱讀 TCP/IP 網絡技術

集群式、負載均衡的RPC框架 rpcx 支持多種的序列化庫,可以有效的減少消息體的大小,但是對于字符串或者圖片的字節slice,明顯還可以進一步的壓縮,正如fasthttp作者valyala在他的新的開源項目 httpteleport 中描述的: 通過1G的帶寬傳輸10G的數據 (夸張)。

為了在RPC的傳輸中減少傳輸的數據大小,我在不影響rpcx整體框架的基礎上,參考了httpteleport的實現,對 net.TCPConn 進行了封裝,實現了壓縮/解壓縮功能的 net.Conn ,可以有效的減少帶寬,節省公司在帶寬上的花費, 以下就是具體的實現。

首先介紹兩種壓縮格式。

zip是常用的一種壓縮格式,Go標準庫中提供了它的 實現 。zip原名Deflate,發明者為菲爾·卡茨(Phil Katz),他于1989年1月公布了該格式的資料。ZIP通常使用后綴名“.zip”,它的MIME格式為application/zip。目前,ZIP格式屬于幾種主流的壓縮格式之一。

snappy(以前稱Zippy)是Google基于 LZ77 的思路用C++語言編寫的快速數據壓縮與解壓程序庫,并在2011年開源。它的目標并非最大壓縮率或與其他壓縮程序庫的兼容性,而是非常高的速度和合理的壓縮率。使用一個運行在64位模式下的酷睿i7處理器的單個核心,壓縮速度250 MB/s,解壓速度500 MB/s。壓縮率比gzip低20-100%。Golang也提供了snappy的 實現 。

所以在壓縮比和速度的權衡中你可以選擇zip格式壓縮或者snappy格式壓縮。

定義這幾種格式:

typeCompressTypebyte

const(
// CompressNone represents no compression
 CompressNone CompressType = iota
// CompressFlate represents zip
 CompressFlate
// CompressSnappy represents snappy
 CompressSnappy
)

然后定義 CompressConn 類型,它嵌入了一個匿名 net.Conn 類型的字段,作為 net.Conn 的包裝,因此它滿足 net.Conn 接口。

其中的 r 、 w 可以實現壓縮讀寫,CompressType定義了壓縮類型。

typeCompressConnstruct{
 net.Conn
 r io.Reader
 w io.Writer
 compressType CompressType
}

覆蓋 net.Conn 的讀寫方法:

func(c *CompressConn) Read(b []byte) (nint, err error) {
returnc.r.Read(b)
}

func(c *CompressConn) Write(b []byte) (nint, err error) {
returnc.w.Write(b)
}

NewCompressConn 是輔助創建方法:

funcNewCompressConn(conn net.Conn, compressType CompressType) net.Conn {
 cc := &CompressConn{Conn: conn}
 r := io.Reader(cc.Conn)

switchcompressType {
caseCompressNone:
caseCompressFlate:
 r = flate.NewReader(r)
caseCompressSnappy:
 r = snappy.NewReader(r)
 }
 cc.r = r

 w := io.Writer(cc.Conn)
switchcompressType {
caseCompressNone:
caseCompressFlate:
 zw, err := flate.NewWriter(w, flate.DefaultCompression)
iferr !=nil{
panic(fmt.Sprintf("BUG: flate.NewWriter(%d) returned non-nil err: %s", flate.DefaultCompression, err))
 }
 w = &writeFlusher{w: zw}
caseCompressSnappy:
 w = snappy.NewWriter(w)
 }
 cc.w = w
returncc
}

對于 zip 格式的寫,寫完后我們需要立即 Flush ,所以也需要對它包裝一下:

typewriteFlusherstruct{
 w *flate.Writer
}

func(wf *writeFlusher) Write(p []byte) (int, error) {
 n, err := wf.w.Write(p)
iferr !=nil{
returnn, err
 }
iferr := wf.w.Flush(); err !=nil{
return0, err
 }
returnn,nil
}

這樣我們就實現了一個可以解壓縮/壓縮的讀寫 net.Conn 對象,你可以通過 NewCompressConn 方法對一個正常的 net.TCPConn 進行包裝,而對它的調用就像一個普通的 net.Conn 一樣。

 

來自:http://colobu.com/2016/11/04/create-a-compressed-TCP-by-Go/

 

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