简单阅读Gzip中间件的源码

Gzip中间件实现的代码很短,加上大量注释也才126行。但如果要看懂它在做什么,事先要看Negroni的源码,来理解这些代码的意义。

Negroni的代码也不长,但是构造很巧妙,一环扣一环,需要一些思考来知道他在干什么。

首先,New()返回的Negroni结构本身是一个包含所有中间件的struct。

而中间件(middleware)是什么呢?他是一个实现了ServeHTTP方法的结构,是一个http.Handler接口的实现。

我们平时实现的http.Handler只是一个Handler对应一个请求,而MiddleWare却可以链式地一层层处理http请求。如何实现的呢?

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

上面就是Middleware的ServeHTTP方法,我们可以看到它实际上并不对请求做任何处理,真正处理请求的是它包裹的Negroni.Handler

这个Handler并不是http.Handler, 只是长得像而已,它的ServeHTTP方法有3个参数,最后多出来的参数是下一个要执行的处理函数(就是下一个中间件的Handler的ServeHTTP)


一个Handler自己的ServeHTTP方法处理完后会执行next函数,将请求传递下去。


Negroni自己的Wrap方法可以把http.Handler转换为Negroni.Handler,可以找源码看看。


好,简单了解了Negroni的中间件的机制后,我们看看Gzip中间件最主要的部分ServeHTTP方法,我们可以在看到整个包实际上就是在实现一个Negroni.Handler:

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()
}

大部分都是注释,handler的本体如下:

type handler struct {
	pool sync.Pool
}
一个对象池,主要是为了来提高效率


首先跳过了两个不需要Gzip压缩的情况,然后从pool中取一个gzip.writer,注释也说了,用defer,在退出或者出错时,把空间还回去,避免多次GC对性能的损耗

之后把它包进gzipResponseWriter里,定义如下:


type gzipResponseWriter struct {
	w *gzip.Writer
	negroni.ResponseWriter
	wroteHeader bool
}

negroni.ResponseWriter包含了http.ResponseWriter,有点像继承的意思。这个可以直接填进next函数当参数。

gzipResponseWriter要实现自己的Write方法:

// Write writes bytes to the gzip.Writer. It will also set the Content-Type
// header using the net/http library content type detection if the Content-Type
// header was not set yet.
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)
}



注意当grw.w也就是gzip.writer指针是空的话,会直接用negroni.ResponseWriter,不进行gzip压缩了。


基本上这个组件就是这样实现了Gzip压缩的功能,具体算法怎么实现的要看"compress/gzip"的代码这就很复杂了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值