1. 程式人生 > >服務計算系列——gzip中介軟體分析

服務計算系列——gzip中介軟體分析

服務計算課程拓展

gzip包分析

寫在前面

以上僅僅為個人總結,如果有錯誤,希望大家可以指出來,以避免舞蹈初學者,謝謝

正文

github連結
這是一個negroni的中介軟體,所以接下來從它的呼叫開始,慢慢來追蹤具體流程,程式碼很短,只有126行而已
1. gzip.Gzip()

func Gzip(level int) *handler {
    h := &handler{}
    h.pool.New = func() interface{} {
        gz, err := gzip.NewWriterLevel(ioutil.Discard, level)
        if
err != nil { panic(err) } return gz } return h }

其實這段程式碼就只是構造了一個handler,這個handler可以之後將進行相應的操作,正如negroni的Use傳入的引數一樣,它需要實現ServeHTTP函式,從而實現相應的介面,具體的gzip內容我們不關注,這裡我們就看看handler的ServeHttp函式的實現
2. ServeHTTP

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http
.Request, next http.HandlerFunc) { // Skip compression if the client doesn't accept gzip encoding. if !strings.Contains(r.Header.Get(headerAcceptEncoding), encodingGzip) { next(w, r) return } // Skip compression if client attempt WebSocket connection if len(r.Header.Get(headerSecWebSocketKey)) > 0
{ next(w, r) return } // Retrieve gzip writer from the pool. Reset it to use the ResponseWriter. // This allows us to re-use an already allocated buffer rather than // allocating a new buffer for every request. // We defer g.pool.Put here so that the gz writer is returned to the // pool if any thing after here fails for some reason (functions in // next could potentially panic, etc) gz := h.pool.Get().(*gzip.Writer) defer h.pool.Put(gz) gz.Reset(w) // Wrap the original http.ResponseWriter with negroni.ResponseWriter // and create the gzipResponseWriter. nrw := negroni.NewResponseWriter(w) grw := gzipResponseWriter{gz, nrw, false} // Call the next handler supplying the gzipResponseWriter instead of // the original. next(&grw, r) // Delete the content length after we know we have been written to. grw.Header().Del(headerContentLength) gz.Close() }

前面兩個就是簡單地跳過不能使用的情況,而後面就是對內容進行壓縮,為了複用壓縮器,也就是避免重新申請空間去使用壓縮器,這裡使用了Reset,這裡我有個疑問,那就是為什麼要把ResponseWriter給包裹起來再傳給下一個中介軟體,這裡就要涉及到golang的繼承問題了,感興趣的可以自己查查瞭解瞭解,還是挺好玩的,具體就體現在這裡的倒數第二段程式碼,你可能會想,grw並沒有Header啊,為什麼可以直接呼叫,其實,是因為grw有nrw,而nrw有w,w具有Header函式,因此grw可以直接呼叫Header(),很神奇是不是?我也覺得,因為我第一次接觸也迷迷糊糊,看到了別人的程式碼和測試了自己的程式碼才相信這是真的。我們注意到,這裡的gzipResponseWriter實現了http.ResponseWriter介面,那我們接下來就對具體的這三個函式進行分析一下
3. Write()

func (grw *gzipResponseWriter) Write(b []byte) (int, error) {
    if !grw.wroteHeader {
        grw.WriteHeader(http.StatusOK)
    }
    if grw.w == nil {
        return grw.ResponseWriter.Write(b)
    }
    if len(grw.Header().Get(headerContentType)) == 0 {
        grw.Header().Set(headerContentType, http.DetectContentType(b))
    }
    return grw.w.Write(b)
}

這裡就是實現了Writer介面的Write函式,首先就是對於Header的處理,之後判斷有沒有定義好的Writer,如果沒有的話,就用原本的Writer來進行處理;如果存在,則用我們定義好的壓縮的Writer來進行資料的寫入。
至於另外兩個函式,一個是直接使用了傳入的w的Header函式,另外一個看名字就知道了,所以這裡不再提及
4. 總結
以上的內容分析結束後我們就差不多解決了這個中介軟體的處理邏輯,即判斷並構建writer->包裝成新的HttpWriter->判斷並處理資料