gzencode 是 workerman 中最稳妥的 gzip 压缩选择,因其生成标准 rfc 1952 格式,含魔数 0x1f 0x8b,兼容浏览器、curl 等;而 gzcompress 输出 raw deflate 数据,无 header 和 checksum,易导致客户端解压失败。

gzencode 是 Workerman 中对用户数据流做 Gzip 压缩最稳妥的选择,而不是 gzcompress 或 zlib_encode。它生成标准 RFC 1952 格式,浏览器、curl、Node.js 客户端等都能原生识别并自动解压,无需额外协议头协商。
为什么不用 gzcompress?
因为 gzcompress 输出的是 raw deflate 数据,没有 gzip header 和 checksum,客户端(尤其是前端 fetch / WebSocket)收到后会解压失败,报错类似 InvalidStateError: Failed to execute 'decode' on 'DecompressionStream' 或静默丢弃。而 gzencode 生成的字节流带完整魔数 0x1f 0x8b,兼容性无死角。
压缩前必须校验数据长度和类型
盲目压缩小数据(比如
- 只对
$data长度 ≥ 256 字节的数据启用gzencode($data, 6) - 跳过已知二进制协议包(如前 4 字节是
0x00 0x00 0x00 0x01这类自定义 header) - 避免重复压缩:若
$data已含0x1f 0x8b开头,直接透传,不套第二层
如何让客户端知道该解压?
Workerman 不走 HTTP 协议栈,所以不能依赖 Content-Encoding: gzip。你得在自定义协议里显式标记:
- 在消息体前加 1 字节 flag:
\x01表示已 gzip,\x00表示未压缩 - 或在 JSON payload 外层加字段:
{"compressed": true, "data": "..."}(但会增加解析开销) - 更推荐二进制协议:包头第 2 字节用 bit mask,bit 0 = compression flag
接收端 decode() 方法里先读 flag,再决定是否调用 gzdecode() —— 这比在 onMessage 里临时判断快且安全。
gzencode 的 level 参数怎么选?
Level 影响 CPU 和压缩率的平衡,实测在 Workerman 高并发场景下:
-
gzencode($data, 1):压缩率低(~20%),但编码耗时仅gzencode($data, 6)的 1/3,适合实时性要求高的心跳、状态推送 -
gzencode($data, 6):默认值,压缩率和性能折中,适用于大多数 JSON/文本数据 - 避免用
9:单次压缩可能卡住事件循环 >10ms,高并发下易引发延迟毛刺
真正影响吞吐的不是压缩率,而是 decode 端能否快速识别并跳过非压缩包 —— 所以协议设计比 level 调优更重要。
协议头识别、长度预判、level 控制这三步漏掉任何一环,压缩就从优化变成负优化。尤其注意别在onMessage 里无差别调用 gzdecode,那是 CPU 火山口。











