
http协议通过accept-encoding请求头允许客户端告知服务器它支持哪些内容编码(如gzip、deflate、br等)。服务器在接收到请求后,可以检查此头部,如果客户端支持gzip,则对响应内容进行压缩,并通过content-encoding: gzip响应头告知客户端内容已被压缩。这种机制被称为内容协商,确保客户端接收到它能处理的最佳内容格式。
为了在Go中实现动态Gzip压缩,我们需要创建一个包装器(Wrapper),它能够:
以下是一个实现Gzip压缩中间件的示例代码:
package main
import (
"compress/gzip"
"io"
"log"
"net/http"
"strings"
)
// gzipWriter 结构体包装了 http.ResponseWriter,用于在写入时进行 Gzip 压缩
type gzipWriter struct {
http.ResponseWriter
Writer io.Writer // 实际的 Gzip 压缩写入器
}
// Write 方法实现了 io.Writer 接口,所有通过此方法写入的数据都会被导向 Gzip 压缩器
func (w *gzipWriter) Write(b []byte) (int, error) {
return w.Writer.Write(b)
}
// WriteHeader 方法设置 HTTP 状态码。
// 在这里,我们确保在响应头中移除 Content-Length,因为压缩后的长度会改变。
func (w *gzipWriter) WriteHeader(code int) {
// 压缩后内容长度发生变化,移除原始的 Content-Length 头部
w.ResponseWriter.Header().Del("Content-Length")
w.ResponseWriter.WriteHeader(code)
}
// GzipHandler 是一个 HTTP 中间件,用于包装原始的 http.Handler,实现 Gzip 压缩
func GzipHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 1. 检查客户端是否支持 Gzip 编码
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r) // 如果不支持,直接调用原始处理器
return
}
// 2. 如果支持 Gzip,则设置 Content-Encoding 头部
w.Header().Set("Content-Encoding", "gzip")
// Content-Type 头部应由原始处理器根据实际内容设置,这里不干预
// 3. 创建 Gzip 写入器
gz := gzip.NewWriter(w)
defer func() {
if err := gz.Close(); err != nil {
log.Printf("Error closing gzip writer: %v", err)
}
}() // 确保 Gzip 写入器关闭并刷新所有待处理的压缩数据
// 4. 创建自定义的 gzipWriter,将 Gzip 写入器作为底层写入器
gzw := &gzipWriter{ResponseWriter: w, Writer: gz}
// 5. 调用原始处理器,但传入我们自定义的 gzipWriter
next.ServeHTTP(gzw, r)
})
}
// 示例处理器:返回一些简单的文本内容
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Write([]byte("Hello, Gzip! This is a compressible response from Go server."))
}
// 示例处理器:返回一个较大的HTML字符串,以更好地展示压缩效果
func largeHTMLHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
htmlContent := `
<!DOCTYPE html>
<html>
<head><title>Large HTML</title></head>
<body>
<h1>Welcome to Gzip Demo</h1>
<p>This is a very long paragraph to demonstrate gzip compression.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
` + strings.Repeat("<p>Repeat this sentence multiple times for better compression ratio.</p>", 50) + `
</body>
</html>`
w.Write([]byte(htmlContent))
}
func main() {
// 注册处理器,并使用 GzipHandler 进行包装
http.Handle("/hello", GzipHandler(http.HandlerFunc(helloHandler)))
http.Handle("/large", GzipHandler(http.HandlerFunc(largeHTMLHandler)))
log.Println("Server starting on :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("Server failed: %v", err)
}
}代码解析:
在main函数中,你可以将任何http.Handler(或http.HandlerFunc)包装到GzipHandler中,然后注册到HTTP路由器:
立即学习“go语言免费学习笔记(深入)”;
http.Handle("/hello", GzipHandler(http.HandlerFunc(helloHandler)))
http.Handle("/large", GzipHandler(http.HandlerFunc(largeHTMLHandler)))当客户端发起请求时,如果其Accept-Encoding头包含gzip,服务器将返回Gzip压缩后的响应。你可以使用curl -H "Accept-Encoding: gzip" http://localhost:8080/large来测试压缩效果,或者通过浏览器开发者工具查看响应头中的Content-Encoding: gzip。
通过上述自定义中间件的方式,Go语言Web应用可以轻松实现动态Gzip内容压缩,而无需依赖复杂的第三方库。这种模式不仅符合Go的简洁哲学,也提供了高度的灵活性和控制力。合理地应用Gzip压缩将显著提升Web服务的性能和用户体验,是现代Web开发中不可或缺的优化手段。
以上就是Go语言Web服务Gzip内容压缩实践指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号