142 lines
3.5 KiB
Markdown
142 lines
3.5 KiB
Markdown
# 协议压缩支持
|
||
|
||
## 概述
|
||
|
||
协议栈支持可选的 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) - 使用高压缩比模式
|
||
- **高 16 位**:原始大小 / 256(用于解压缓冲区分配)
|
||
|
||
## 使用方法
|
||
|
||
### C 端
|
||
|
||
```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 端
|
||
|
||
```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 端
|
||
|
||
```bash
|
||
# 安装 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% |
|
||
|
||
## 调试
|
||
|
||
启用调试模式可以看到压缩详情:
|
||
|
||
```bash
|
||
# C 端
|
||
make DEBUG=1
|
||
|
||
# 运行后会输出类似:
|
||
# [DEBUG] 压缩成功: 1024 -> 412 (40.2%)
|
||
# [DEBUG] 解压成功: 412 -> 1024 字节
|
||
```
|