3.5 KiB
3.5 KiB
协议压缩支持
概述
协议栈支持可选的 LZ4 压缩,可以显著减少网络带宽消耗,特别适用于终端输出等重复性较高的数据。
压缩算法选择
我们选择 LZ4 作为压缩算法,原因如下:
| 特性 | LZ4 | zlib | zstd |
|---|---|---|---|
| 压缩速度 | ~500 MB/s | ~50 MB/s | ~300 MB/s |
| 解压速度 | ~1500 MB/s | ~250 MB/s | ~800 MB/s |
| 压缩比 | 2:1 ~ 3:1 | 3:1 ~ 5:1 | 3:1 ~ 5:1 |
| 延迟影响 | 极低 | 中等 | 低 |
对于终端数据流,LZ4 的极低延迟和高速度是最佳选择。
消息头格式
+--------+--------+------------+------------+
| Magic | Type | PayloadLen | Reserved |
| 4字节 | 4字节 | 4字节 | 4字节 |
+--------+--------+------------+------------+
Reserved 字段用法:
- 低 16 位:压缩标志
- bit 0:
MSG_FLAG_COMPRESSED(0x01) - 载荷已压缩 - bit 1:
MSG_FLAG_COMPRESS_LZ4(0x02) - 使用 LZ4 压缩 - bit 2:
MSG_FLAG_COMPRESS_HC(0x04) - 使用高压缩比模式
- bit 0:
- 高 16 位:原始大小 / 256(用于解压缓冲区分配)
使用方法
C 端
#include "socket_protocol.h"
#include "compression.h"
// 初始化协议上下文(启用 LZ4 压缩)
ProtocolContext ctx;
protocol_init(&ctx, COMPRESS_LZ4, 0); // 0 = 默认级别
// 发送压缩消息
write_message_compressed(sock, &ctx, MSG_TYPE_TERMINAL_OUTPUT, data, data_len);
// 接收并解压消息
MessageType type;
void* payload;
uint32_t payload_len, original_len;
read_message_decompressed(sock, &type, &payload, &payload_len, &original_len);
// 获取压缩统计
uint64_t in, out, count, skip;
compression_get_stats(&ctx.compress_ctx, &in, &out, &count, &skip);
printf("压缩比: %d%%\n", compression_get_ratio(&ctx.compress_ctx));
Go 端
import "go_service/internal/socket"
// 创建带压缩的连接
conn := socket.NewConnectionWithCompression(netConn, socket.CompressLZ4, 0)
// 或者在现有连接上启用压缩
conn.EnableCompression(socket.CompressLZ4, 0)
// 发送压缩消息
ctx := socket.NewCompressionContext(socket.CompressLZ4, 0)
socket.WriteMessageCompressed(netConn, ctx, socket.MsgTypeServerResponse, data)
// 接收并自动解压
msgType, payload, err := socket.ReadMessageWithDecompression(netConn)
// 获取统计信息
bytesIn, bytesOut, compCount, skipCount, ratio := conn.GetCompressionStats()
fmt.Printf("压缩比: %d%%\n", ratio)
编译配置
C 端
# 安装 LZ4 库
make install-lz4
# 编译(自动检测 LZ4)
make
# 禁用压缩
make LZ4=0
# 查看是否启用了压缩
make pre_build
Go 端
LZ4 库会自动通过 go mod tidy 安装。
压缩阈值
- 小于 64 字节的数据不会被压缩(开销大于收益)
- 如果压缩后数据更大,会自动回退到原始数据
兼容性
- 新版本可以读取旧版本(无压缩)的消息
- 旧版本可以读取新版本的未压缩消息
- 旧版本无法读取压缩消息(会因为 reserved 字段不为 0 而有不同行为,但基本功能仍可工作)
性能预期
典型终端场景压缩效果:
| 数据类型 | 原始大小 | 压缩后 | 节省 |
|---|---|---|---|
| ls 输出 | 1000 B | 400 B | 60% |
| 代码文件 | 10 KB | 3.5 KB | 65% |
| 日志输出 | 5 KB | 1.2 KB | 76% |
| 二进制数据 | 1 KB | 900 B | 10% |
调试
启用调试模式可以看到压缩详情:
# C 端
make DEBUG=1
# 运行后会输出类似:
# [DEBUG] 压缩成功: 1024 -> 412 (40.2%)
# [DEBUG] 解压成功: 412 -> 1024 字节