http/2 的多路复用通过二进制分帧、流(stream)和 hpack 头部压缩实现:在单条 tcp 连接上并发传输多个带唯一 stream id 的请求/响应帧,消除队头阻塞、减少连接数、压缩重复头部。

Go 本身没有“压缩的多路复用网络套接字”这种原生抽象——net.Conn 是裸 TCP/UDP 连接,不带压缩;HTTP/2 的多路复用是应用层协议,不是 socket 层功能。你要的其实是:在 HTTP/2 或自定义二进制协议上,实现流式压缩 + 并发请求处理。关键不在封装 socket,而在选对协议栈和压缩时机。
HTTP/2 服务端开启 gzip 压缩时,为什么响应没被压缩?
常见错误是只设了 Content-Encoding: gzip 头,但没真正用 gzip.Writer 写响应体。HTTP/2 本身不自动压缩,它只负责多路复用和 HPACK 头部压缩,body 压缩仍需手动介入。
- 必须显式用
gzip.NewWriter(w)包装http.ResponseWriter,再写入数据 -
w.Header().Set("Content-Encoding", "gzip")要在gzip.Writer创建前设置,否则部分中间件(如 gorilla/handlers)可能覆盖 - 如果用了
http.StripPrefix或反向代理,确保中间件不缓冲响应、不提前关闭 writer - HTTP/2 下,多个请求共用一条连接,每个响应仍需独立压缩,不能复用同一个
gzip.Writer
用 archive/zip 流式打包多文件时,如何避免阻塞其他 HTTP 请求?
核心是别在 handler 里做同步 I/O 和 CPU 密集操作。ZIP 打包涉及文件读取、CRC 计算、Deflate 压缩,容易拖慢整个 goroutine。
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
- 用
io.Copy配合bufio.NewReader读文件,减少系统调用次数 - 对每个待打包文件,先
os.Stat()检查存在性和大小,跳过超限文件(如 >500MB),防止 OOM - 压缩前调用
filepath.Clean()规范路径,再用strings.HasPrefix()白名单校验,防止路径遍历 - 不要在 handler 中直接调用
zip.Writer.Close()后继续写响应——它会 flush 并写 ZIP 结束标记,之后再写就 panic
自定义二进制协议下,怎么给每个 stream 加 gzip 压缩?
HTTP/2 的 stream 是逻辑通道,不是独立 socket。你无法对单个 stream 单独启用 gzip;压缩必须发生在应用层序列化之后、帧写入之前。
- 典型流程:结构体 →
json.Marshal或proto.Marshal→gzip.NewWriter→ 写入http.ResponseWriter(HTTP/2 场景)或自定义io.Writer(gRPC 或私有协议) - 不要尝试压缩原始 TCP
net.Conn—— 会破坏 HTTP/2 帧格式,导致连接重置 - 若用 gRPC,直接依赖
grpc.WithCompressor(gzip.Compressor{}),它会在编码后、发送前压缩 payload - 自己实现时,务必为每个消息单独 new
gzip.Writer,用完立即Close(),否则内存泄漏
最易被忽略的是:gzip 压缩不是免费的。小文件(
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










