服务计算系列——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->判断并处理数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值