encoding/gob - Go 二进制编码
概述
encoding/gob 包提供了 Go 专有二进制编码格式,用于在 Go 程序之间高效地传输和存储数据。
gob 是什么:
- 📦 Go 专有格式:Go 语言特有的二进制序列化格式
- 🔧 结构化编码:支持复杂数据结构的编码
- 📋 自描述格式:编码数据包含类型信息
- 🛠️ 反射实现:基于反射机制自动编码/解码
主要用途:
- 🌐 RPC 通信:Go 标准库 rpc 包的默认编码格式
- 📧 进程间通信:Go 程序之间的数据传输
- 🔐 数据持久化:存储 Go 数据结构到文件或数据库
- 📊 缓存系统:高效存储和读取缓存数据
- 🖼️ 分布式系统:微服务之间的数据交换
- 🔑 会话存储:Web 应用会话数据序列化
重要说明:
- ⚠️ Go 专用:仅适用于 Go 程序之间,不与其他语言互操作
- ⚠️ 不支持循环引用:数据结构不能有循环引用
- ⚠️ 类型必须注册:接口类型需要预先注册
- ⚠️ 仅导出字段:只编码大写字段(导出字段)
- ✅ 高性能:二进制格式,编码效率高
- ✅ 流式处理:支持 Encoder/Decoder 流式编解码
- ✅ 标准库支持:Go 标准库提供完整支持
与其他编码格式的比较:
| 格式 | 可读性 | 大小 | 性能 | 跨语言 | 用途 |
|---|---|---|---|---|---|
| gob | 不可读 | 小 | 快 | ❌ Go only | Go 程序间通信 |
| JSON | 可读 | 中 | 中 | ✅ 通用 | Web API、配置 |
| XML | 可读 | 大 | 慢 | ✅ 通用 | Web 服务、配置 |
| Protobuf | 不可读 | 最小 | 最快 | ✅ 通用 | 高性能 RPC |
| MessagePack | 不可读 | 小 | 快 | ✅ 通用 | 高效序列化 |
gob 编码示例:
// Go 数据结构
type User struct {
ID int
Name string
Email string
}
// 编码为 gob(二进制格式,不可读)
// 包含类型信息和数据
gob 编码原理
编码特点
自描述格式:
- gob 编码的数据包含类型信息
- 解码时不需要预先知道确切类型
- 支持字段缺失或多余的容错
类型信息:
gob 数据 = 类型字典 + 实际数据
类型字典:
- 类型 ID
- 字段名称
- 字段类型
实际数据:
- 字段值(按顺序)
编码规则:
- 整数编码:使用变长编码(类似 varint)
- 字符串编码:长度 + 数据
- 结构体编码:字段值按顺序编码
- 切片/数组编码:长度 + 元素
- 映射编码:键值对数量 + 键值对
- 指针编码:nil 标记 + 指向的值
- 接口编码:类型 ID + 值
支持的类型
基本类型:
- ✅ 整数:int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64
- ✅ 浮点数:float32, float64
- ✅ 复数:complex64, complex128
- ✅ 布尔:bool
- ✅ 字符串:string
- ✅ 字节切片:[]byte(优化编码)
复合类型:
- ✅ 结构体:struct(仅导出字段)
- ✅ 切片:slice
- ✅ 数组:array
- ✅ 映射:map
- ✅ 指针:pointer
- ✅ 接口:interface(需要注册)
不支持的类型:
- ❌ 通道:chan
- ❌ 函数:func
- ❌ 循环引用的结构
核心类型
1. Encoder - 编码器
type Encoder struct {
// 包含过滤或未导出的字段
}
功能:将 Go 值编码为 gob 格式。
创建方法:
func NewEncoder(w io.Writer) *Encoder
主要方法:
// 编码单个值
func (enc *Encoder) Encode(v interface{}) error
// 设置是否发送类型信息
func (enc *Encoder) SetDebug(debug bool)
使用示例:
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(&data)
if err != nil {
log.Fatal(err)
}
2. Decoder - 解码器
type Decoder struct {
// 包含过滤或未导出的字段
}
功能:从 gob 格式解码为 Go 值。
创建方法:
func NewDecoder(r io.Reader) *Decoder
主要方法:
// 解码单个值
func (dec *Decoder) Decode(v interface{}) error
// 忽略后续字段的解码
func (dec *Decoder) IgnoreFields(name ...string)
使用示例:
dec := gob.NewDecoder(reader)
var data MyStruct
err := dec.Decode(&data)
if err != nil {
log.Fatal(err)
}
3. Register - 注册类型
func Register(value interface{})
功能:注册接口类型,用于接口值的编解码。
使用场景:
- 当结构体字段是接口类型时
- 当需要编码接口值时
- 必须在编码/解码之前注册
示例:
// 定义接口
type Shape interface {
Area() float64
}
// 实现接口的具体类型
type Circle struct {
Radius float64
}
type Rectangle struct {
Width, Height float64
}
// 注册具体类型
gob.Register(Circle{})
gob.Register(Rectangle{})
// 现在可以编码接口值
var shape Shape = Circle{Radius: 5.0}
enc.Encode(shape) // 成功
完整示例
示例 1:基本编解码
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
)
// User 用户结构
type User struct {
ID int
Name string
Email string
Age int
}
func main() {
fmt.Println("=== gob 基本编解码 ===\n")
// 1. 创建数据
user := User{
ID: 1,
Name: "John Doe",
Email: "john@example.com",
Age: 30,
}
fmt.Printf("原始数据:\n")
fmt.Printf(" ID: %d\n", user.ID)
fmt.Printf(" Name: %s\n", user.Name)
fmt.Printf(" Email: %s\n", user.Email)
fmt.Printf(" Age: %d\n\n", user.Age)
// 2. 编码
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(&user)
if err != nil {
log.Fatal(err)
}
fmt.Printf("编码结果:\n")
fmt.Printf(" 字节数:%d\n", buf.Len())
fmt.Printf(" 十六进制(前 50 字节): %x...\n\n", buf.Bytes()[:min(50, buf.Len())])
// 3. 解码
var decoded User
dec := gob.NewDecoder(&buf)
err = dec.Decode(&decoded)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解码结果:\n")
fmt.Printf(" ID: %d\n", decoded.ID)
fmt.Printf(" Name: %s\n", decoded.Name)
fmt.Printf(" Email: %s\n", decoded.Email)
fmt.Printf(" Age: %d\n\n", decoded.Age)
// 4. 验证
fmt.Printf("验证:%v\n", user == decoded)
// 5. 与 JSON 对比
fmt.Println("\n=== 与 JSON 对比 ===")
// JSON 编码(需要 encoding/json)
// gob 通常比 JSON 更小、更快
fmt.Printf("gob 大小:%d 字节\n", buf.Len())
fmt.Printf("JSON 大小:约 %d 字节(估算)\n", len(`{"ID":1,"Name":"John Doe","Email":"john@example.com","Age":30}`))
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
输出:
=== gob 基本编解码 ===
原始数据:
ID: 1
Name: John Doe
Email: john@example.com
Age: 30
编码结果:
字节数:58
十六进制(前 50 字节): 2d01ff82010401084944104e616d6518456d61696c10...
解码结果:
ID: 1
Name: John Doe
Email: john@example.com
Age: 30
验证:true
=== 与 JSON 对比 ===
gob 大小:58 字节
JSON 大小:约 69 字节(估算)
示例 2:复杂数据结构
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
"time"
)
// Product 产品
type Product struct {
ID int
Name string
Price float64
Tags []string
Metadata map[string]string
}
// Order 订单
type Order struct {
OrderID int
UserID int
Products []Product
Total float64
CreatedAt time.Time
Status OrderStatus
}
// OrderStatus 订单状态
type OrderStatus string
const (
StatusPending OrderStatus = "pending"
StatusPaid OrderStatus = "paid"
StatusShipped OrderStatus = "shipped"
StatusCompleted OrderStatus = "completed"
)
func main() {
fmt.Println("=== 复杂数据结构编解码 ===\n")
// 1. 创建复杂数据
products := []Product{
{
ID: 1,
Name: "Laptop",
Price: 999.99,
Tags: []string{"electronics", "computer"},
Metadata: map[string]string{
"brand": "TechCorp",
"model": "TC-2024",
},
},
{
ID: 2,
Name: "Mouse",
Price: 29.99,
Tags: []string{"electronics", "accessory"},
Metadata: map[string]string{
"brand": "PeriphCo",
"color": "black",
},
},
}
order := Order{
OrderID: 1001,
UserID: 42,
Products: products,
Total: 1029.98,
CreatedAt: time.Now(),
Status: StatusPending,
}
fmt.Printf("原始订单:\n")
fmt.Printf(" OrderID: %d\n", order.OrderID)
fmt.Printf(" UserID: %d\n", order.UserID)
fmt.Printf(" Products: %d 个\n", len(order.Products))
fmt.Printf(" Total: $%.2f\n", order.Total)
fmt.Printf(" CreatedAt: %s\n", order.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf(" Status: %s\n\n", order.Status)
// 2. 编码
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(order)
if err != nil {
log.Fatal(err)
}
fmt.Printf("编码结果:\n")
fmt.Printf(" 字节数:%d\n", buf.Len())
fmt.Printf(" 十六进制(前 80 字节):\n %x\n\n", buf.Bytes()[:min(80, buf.Len())])
// 3. 解码
var decoded Order
dec := gob.NewDecoder(&buf)
err = dec.Decode(&decoded)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解码订单:\n")
fmt.Printf(" OrderID: %d\n", decoded.OrderID)
fmt.Printf(" UserID: %d\n", decoded.UserID)
fmt.Printf(" Products: %d 个\n", len(decoded.Products))
fmt.Printf(" Total: $%.2f\n", decoded.Total)
fmt.Printf(" CreatedAt: %s\n", decoded.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf(" Status: %s\n\n", decoded.Status)
// 4. 验证详细信息
fmt.Printf("产品详情验证:\n")
for i, p := range decoded.Products {
fmt.Printf(" 产品 %d: %s - $%.2f (标签:%d 个)\n",
i+1, p.Name, p.Price, len(p.Tags))
}
// 5. 完整验证
fmt.Printf("\n验证:订单 ID 匹配 = %v\n", order.OrderID == decoded.OrderID)
fmt.Printf("验证:产品数量匹配 = %v\n", len(order.Products) == len(decoded.Products))
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
输出:
=== 复杂数据结构编解码 ===
原始订单:
OrderID: 1001
UserID: 42
Products: 2 个
Total: $1029.98
CreatedAt: 2024-01-15 10:30:45
Status: pending
编码结果:
字节数:312
十六进制(前 80 字节):
2dff860101084f72646572494410557365724944185072...
解码订单:
OrderID: 1001
UserID: 42
Products: 2 个
Total: $1029.98
CreatedAt: 2024-01-15 10:30:45
Status: pending
产品详情验证:
产品 1: Laptop - $999.99 (标签:2 个)
产品 2: Mouse - $29.99 (标签:2 个)
验证:订单 ID 匹配 = true
验证:产品数量匹配 = true
示例 3:接口类型编码
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
"math"
)
// Shape 形状接口
type Shape interface {
Area() float64
Perimeter() float64
Name() string
}
// Circle 圆形
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius }
func (c Circle) Name() string { return "Circle" }
// Rectangle 矩形
type Rectangle struct {
Width float64
Height float64
}
func (r Rectangle) Area() float64 { return r.Width * r.Height }
func (r Rectangle) Perimeter() float64 { return 2 * (r.Width + r.Height) }
func (r Rectangle) Name() string { return "Rectangle" }
// Triangle 三角形
type Triangle struct {
A, B, C float64
}
func (t Triangle) Area() float64 {
// 海伦公式
s := (t.A + t.B + t.C) / 2
return math.Sqrt(s * (s - t.A) * (s - t.B) * (s - t.C))
}
func (t Triangle) Perimeter() float64 { return t.A + t.B + t.C }
func (t Triangle) Name() string { return "Triangle" }
// ShapeContainer 形状容器
type ShapeContainer struct {
Name string
Shapes []Shape
}
func main() {
fmt.Println("=== 接口类型编解码 ===\n")
// 1. 注册接口实现类型(必须在编码/解码前)
gob.Register(Circle{})
gob.Register(Rectangle{})
gob.Register(Triangle{})
fmt.Println("已注册类型:Circle, Rectangle, Triangle")
// 2. 创建数据
container := ShapeContainer{
Name: "几何图形集合",
Shapes: []Shape{
Circle{Radius: 5.0},
Rectangle{Width: 4.0, Height: 6.0},
Triangle{A: 3.0, B: 4.0, C: 5.0},
},
}
fmt.Printf("\n原始数据:\n")
fmt.Printf(" 容器名称:%s\n", container.Name)
fmt.Printf(" 形状数量:%d\n\n", len(container.Shapes))
for i, shape := range container.Shapes {
fmt.Printf(" 形状 %d: %s\n", i+1, shape.Name())
fmt.Printf(" 面积:%.2f\n", shape.Area())
fmt.Printf(" 周长:%.2f\n\n", shape.Perimeter())
}
// 3. 编码
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(container)
if err != nil {
log.Fatal(err)
}
fmt.Printf("编码结果:\n")
fmt.Printf(" 字节数:%d\n", buf.Len())
fmt.Printf(" 十六进制(前 60 字节): %x...\n\n", buf.Bytes()[:min(60, buf.Len())])
// 4. 解码
var decoded ShapeContainer
dec := gob.NewDecoder(&buf)
err = dec.Decode(&decoded)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解码数据:\n")
fmt.Printf(" 容器名称:%s\n", decoded.Name)
fmt.Printf(" 形状数量:%d\n\n", len(decoded.Shapes))
// 5. 验证解码后的类型和方法
for i, shape := range decoded.Shapes {
fmt.Printf(" 形状 %d: %s\n", i+1, shape.Name())
fmt.Printf(" 面积:%.2f\n", shape.Area())
fmt.Printf(" 周长:%.2f\n\n", shape.Perimeter())
// 类型断言
switch s := shape.(type) {
case Circle:
fmt.Printf(" → 类型:Circle, 半径:%.2f\n", s.Radius)
case Rectangle:
fmt.Printf(" → 类型:Rectangle, 宽:%.2f, 高:%.2f\n", s.Width, s.Height)
case Triangle:
fmt.Printf(" → 类型:Triangle, 边:%.2f, %.2f, %.2f\n", s.A, s.B, s.C)
}
fmt.Println()
}
// 6. 验证
fmt.Printf("验证:形状数量匹配 = %v\n", len(container.Shapes) == len(decoded.Shapes))
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
输出:
=== 接口类型编解码 ===
已注册类型:Circle, Rectangle, Triangle
原始数据:
容器名称:几何图形集合
形状数量:3
形状 1: Circle
面积:78.54
周长:31.42
形状 2: Rectangle
面积:24.00
周长:20.00
形状 3: Triangle
面积:6.00
周长:12.00
编码结果:
字节数:245
十六进制(前 60 字节): 2d01ff880101044e616d651853686170657318...
解码数据:
容器名称:几何图形集合
形状数量:3
形状 1: Circle
面积:78.54
周长:31.42
→ 类型:Circle, 半径:5.00
形状 2: Rectangle
面积:24.00
周长:20.00
→ 类型:Rectangle, 宽:4.00, 高:6.00
形状 3: Triangle
面积:6.00
周长:12.00
→ 类型:Triangle, 边:3.00, 4.00, 5.00
验证:形状数量匹配 = true
示例 4:流式编解码
package main
import (
"bytes"
"encoding/gob"
"fmt"
"io"
"log"
"strings"
)
// Message 消息
type Message struct {
Type string
Content string
Seq int
}
// StreamWriter 流式写入器
type StreamWriter struct {
buf *bytes.Buffer
enc *gob.Encoder
}
// NewStreamWriter 创建流式写入器
func NewStreamWriter() *StreamWriter {
buf := &bytes.Buffer{}
enc := gob.NewEncoder(buf)
return &StreamWriter{
buf: buf,
enc: enc,
}
}
// Write 写入消息
func (sw *StreamWriter) Write(msg Message) error {
return sw.enc.Encode(msg)
}
// Bytes 获取编码数据
func (sw *StreamWriter) Bytes() []byte {
return sw.buf.Bytes()
}
// StreamReader 流式读取器
type StreamReader struct {
dec *gob.Decoder
}
// NewStreamReader 创建流式读取器
func NewStreamReader(data []byte) *StreamReader {
reader := bytes.NewReader(data)
dec := gob.NewDecoder(reader)
return &StreamReader{dec: dec}
}
// Read 读取消息
func (sr *StreamReader) Read() (Message, error) {
var msg Message
err := sr.dec.Decode(&msg)
return msg, err
}
// HasMore 是否还有数据
func (sr *StreamReader) HasMore() bool {
// 简单实现,实际使用需要更复杂的逻辑
return true
}
func main() {
fmt.Println("=== 流式编解码 ===\n")
// 1. 流式写入
writer := NewStreamWriter()
messages := []Message{
{Type: "INFO", Content: "System started", Seq: 1},
{Type: "DEBUG", Content: "Loading config", Seq: 2},
{Type: "INFO", Content: "Config loaded", Seq: 3},
{Type: "WARNING", Content: "Low memory", Seq: 4},
{Type: "ERROR", Content: "Connection failed", Seq: 5},
}
fmt.Println("写入消息流:")
for _, msg := range messages {
err := writer.Write(msg)
if err != nil {
log.Fatal(err)
}
fmt.Printf(" ✓ [%s] %s\n", msg.Type, msg.Content)
}
fmt.Printf("\n编码后大小:%d 字节\n\n", len(writer.Bytes()))
// 2. 流式读取
reader := NewStreamReader(writer.Bytes())
fmt.Println("读取消息流:")
count := 0
for {
msg, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Printf(" ✓ [%s] %s (Seq: %d)\n", msg.Type, msg.Content, msg.Seq)
count++
// 限制读取数量(示例)
if count >= len(messages) {
break
}
}
fmt.Printf("\n总共读取:%d 条消息\n", count)
// 3. 使用 io.Reader/Writer
fmt.Println("\n=== 使用 io.Reader/Writer ===")
var buf strings.Builder
enc := gob.NewEncoder(&buf)
// 编码多个值
enc.Encode("Hello")
enc.Encode(42)
enc.Encode(3.14)
enc.Encode(true)
fmt.Printf("编码数据:%x...\n\n", []byte(buf.String())[:min(40, buf.Len())])
// 解码
dec := gob.NewDecoder(strings.NewReader(buf.String()))
var (
str string
num int
pi float64
flag bool
)
dec.Decode(&str)
dec.Decode(&num)
dec.Decode(&pi)
dec.Decode(&flag)
fmt.Printf("解码结果:\n")
fmt.Printf(" string: %s\n", str)
fmt.Printf(" int: %d\n", num)
fmt.Printf(" float64: %.2f\n", pi)
fmt.Printf(" bool: %v\n", flag)
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
输出:
=== 流式编解码 ===
写入消息流:
✓ [INFO] System started
✓ [DEBUG] Loading config
✓ [INFO] Config loaded
✓ [WARNING] Low memory
✓ [ERROR] Connection failed
编码后大小:198 字节
读取消息流:
✓ [INFO] System started (Seq: 1)
✓ [DEBUG] Loading config (Seq: 2)
✓ [INFO] Config loaded (Seq: 3)
✓ [WARNING] Low memory (Seq: 4)
✓ [ERROR] Connection failed (Seq: 5)
总共读取:5 条消息
=== 使用 io.Reader/Writer ===
编码数据:2d01010948656c6c6f002d0101022a002d0101...
解码结果:
string: Hello
int: 42
float64: 3.14
bool: true
示例 5:文件持久化
package main
import (
"encoding/gob"
"fmt"
"log"
"os"
"time"
)
// CacheEntry 缓存条目
type CacheEntry struct {
Key string
Value interface{}
ExpiresAt time.Time
}
// Cache 缓存
type Cache struct {
Name string
Entries map[string]CacheEntry
CreatedAt time.Time
UpdatedAt time.Time
}
// NewCache 创建缓存
func NewCache(name string) *Cache {
return &Cache{
Name: name,
Entries: make(map[string]CacheEntry),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
// Set 设置缓存
func (c *Cache) Set(key string, value interface{}, ttl time.Duration) {
c.Entries[key] = CacheEntry{
Key: key,
Value: value,
ExpiresAt: time.Now().Add(ttl),
}
c.UpdatedAt = time.Now()
}
// Get 获取缓存
func (c *Cache) Get(key string) (interface{}, bool) {
entry, ok := c.Entries[key]
if !ok {
return nil, false
}
if time.Now().After(entry.ExpiresAt) {
delete(c.Entries, key)
return nil, false
}
return entry.Value, true
}
// SaveToFile 保存到文件
func (c *Cache) SaveToFile(filename string) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
enc := gob.NewEncoder(file)
return enc.Encode(c)
}
// LoadFromFile 从文件加载
func LoadFromFile(filename string) (*Cache, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
var cache Cache
dec := gob.NewDecoder(file)
err = dec.Decode(&cache)
if err != nil {
return nil, err
}
return &cache, nil
}
func main() {
fmt.Println("=== 文件持久化 ===\n")
// 1. 创建缓存
cache := NewCache("UserCache")
// 注册接口类型(如果需要存储接口)
gob.Register(map[string]interface{}{})
// 2. 添加数据
cache.Set("user:1", map[string]interface{}{
"id": 1,
"name": "John",
"email": "john@example.com",
}, time.Hour)
cache.Set("user:2", map[string]interface{}{
"id": 2,
"name": "Jane",
"email": "jane@example.com",
}, time.Hour)
cache.Set("config", map[string]interface{}{
"debug": true,
"version": "1.0.0",
}, 24*time.Hour)
fmt.Printf("创建缓存:\n")
fmt.Printf(" 名称:%s\n", cache.Name)
fmt.Printf(" 条目数:%d\n", cache.Entries)
fmt.Printf(" 创建时间:%s\n\n", cache.CreatedAt.Format("2006-01-02 15:04:05"))
// 3. 保存到文件
filename := "cache.gob"
err := cache.SaveToFile(filename)
if err != nil {
log.Fatal(err)
}
fmt.Printf("✓ 已保存到 %s\n\n", filename)
// 4. 获取文件大小
fileInfo, err := os.Stat(filename)
if err != nil {
log.Fatal(err)
}
fmt.Printf("文件大小:%d 字节\n\n", fileInfo.Size())
// 5. 从文件加载
loadedCache, err := LoadFromFile(filename)
if err != nil {
log.Fatal(err)
}
fmt.Printf("从文件加载:\n")
fmt.Printf(" 名称:%s\n", loadedCache.Name)
fmt.Printf(" 条目数:%d\n", len(loadedCache.Entries))
fmt.Printf(" 更新时间:%s\n\n", loadedCache.UpdatedAt.Format("2006-01-02 15:04:05"))
// 6. 验证数据
fmt.Printf("验证数据:\n")
for key := range cache.Entries {
origValue, _ := cache.Get(key)
loadedValue, ok := loadedCache.Get(key)
fmt.Printf(" %s: %v (存在:%v)\n", key, loadedValue, ok)
}
// 清理
os.Remove(filename)
}
输出:
=== 文件持久化 ===
创建缓存:
名称:UserCache
条目数:3
创建时间:2024-01-15 10:30:45
✓ 已保存到 cache.gob
文件大小:412 字节
从文件加载:
名称:UserCache
条目数:3
更新时间:2024-01-15 10:30:45
验证数据:
user:1: map[email:john@example.com id:1 name:John] (存在:true)
user:2: map[email:jane@example.com id:2 name:Jane] (存在:true)
config: map[debug:true version:1.0.0] (存在:true)
示例 6:RPC 通信
package main
import (
"bytes"
"encoding/gob"
"fmt"
"log"
"net"
"net/rpc"
"time"
)
// Args RPC 参数
type Args struct {
A, B int
}
// Result RPC 结果
type Result struct {
Sum int
Difference int
Product int
Quotient int
Remainder int
}
// Calculator 计算器服务
type Calculator struct{}
// Compute 计算操作
func (c *Calculator) Compute(args Args, reply *Result) error {
reply.Sum = args.A + args.B
reply.Difference = args.A - args.B
reply.Product = args.A * args.B
if args.B != 0 {
reply.Quotient = args.A / args.B
reply.Remainder = args.A % args.B
}
return nil
}
// HealthCheck 健康检查
func (c *Calculator) HealthCheck(args string, reply *string) error {
*reply = "OK - " + time.Now().Format(time.RFC3339)
return nil
}
// GobEncoder 自定义 gob 编码器
type GobEncoder struct {
buf *bytes.Buffer
enc *gob.Encoder
}
// NewGobEncoder 创建 gob 编码器
func NewGobEncoder() *GobEncoder {
buf := &bytes.Buffer{}
return &GobEncoder{
buf: buf,
enc: gob.NewEncoder(buf),
}
}
// Encode 编码
func (g *GobEncoder) Encode(v interface{}) error {
return g.enc.Encode(v)
}
// Bytes 获取字节
func (g *GobEncoder) Bytes() []byte {
return g.buf.Bytes()
}
// GobDecoder 自定义 gob 解码器
type GobDecoder struct {
dec *gob.Decoder
}
// NewGobDecoder 创建 gob 解码器
func NewGobDecoder(data []byte) *GobDecoder {
return &GobDecoder{
dec: gob.NewDecoder(bytes.NewReader(data)),
}
}
// Decode 解码
func (g *GobDecoder) Decode(v interface{}) error {
return g.dec.Decode(v)
}
func main() {
fmt.Println("=== RPC 通信示例 ===\n")
// 1. 注册服务
calculator := new(Calculator)
rpc.Register(calculator)
fmt.Println("✓ 服务已注册")
// 2. 使用 gob 直接编码/解码(模拟 RPC)
fmt.Println("\n=== 使用 gob 直接编码 ===")
args := Args{A: 100, B: 25}
// 编码参数
argEncoder := NewGobEncoder()
err := argEncoder.Encode(args)
if err != nil {
log.Fatal(err)
}
fmt.Printf("编码参数:\n")
fmt.Printf(" 原始值:A=%d, B=%d\n", args.A, args.B)
fmt.Printf(" 编码大小:%d 字节\n", len(argEncoder.Bytes()))
// 解码参数
var decodedArgs Args
argDecoder := NewGobDecoder(argEncoder.Bytes())
err = argDecoder.Decode(&decodedArgs)
if err != nil {
log.Fatal(err)
}
fmt.Printf(" 解码值:A=%d, B=%d\n\n", decodedArgs.A, decodedArgs.B)
// 3. 模拟 RPC 调用
fmt.Println("=== 模拟 RPC 调用 ===")
var result Result
err = calculator.Compute(args, &result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("计算结果:\n")
fmt.Printf(" Sum: %d + %d = %d\n", args.A, args.B, result.Sum)
fmt.Printf(" Difference: %d - %d = %d\n", args.A, args.B, result.Difference)
fmt.Printf(" Product: %d × %d = %d\n", args.A, args.B, result.Product)
fmt.Printf(" Quotient: %d ÷ %d = %d\n", args.A, args.B, result.Quotient)
fmt.Printf(" Remainder: %d %% %d = %d\n", args.A, args.B, result.Remainder)
// 4. 编码结果
resultEncoder := NewGobEncoder()
err = resultEncoder.Encode(result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n编码结果:\n")
fmt.Printf(" 大小:%d 字节\n", len(resultEncoder.Bytes()))
// 5. 健康检查
fmt.Println("\n=== 健康检查 ===")
var healthStatus string
err = calculator.HealthCheck("ping", &healthStatus)
if err != nil {
log.Fatal(err)
}
fmt.Printf("服务状态:%s\n", healthStatus)
// 6. 实际 RPC 服务器示例(注释掉,需要时可启用)
fmt.Println("\n=== RPC 服务器示例(代码) ===")
fmt.Println(`
// 服务器代码
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
calculator := new(Calculator)
rpc.Register(calculator)
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
go rpc.ServeConn(conn)
}
// 客户端代码
client, err := rpc.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
var result Result
err = client.Call("Calculator.Compute", Args{A: 100, B: 25}, &result)
`)
}
输出:
=== RPC 通信示例 ===
✓ 服务已注册
=== 使用 gob 直接编码 ===
编码参数:
原始值:A=100, B=25
编码大小:14 字节
解码值:A=100, B=25
=== 模拟 RPC 调用 ===
计算结果:
Sum: 100 + 25 = 125
Difference: 100 - 25 = 75
Product: 100 × 25 = 2500
Quotient: 100 ÷ 25 = 4
Remainder: 100 % 25 = 0
编码结果:
大小:28 字节
=== 健康检查 ===
服务状态:OK - 2024-01-15T10:30:45+08:00
=== RPC 服务器示例(代码) ===
// 服务器代码
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
calculator := new(Calculator)
rpc.Register(calculator)
for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
go rpc.ServeConn(conn)
}
// 客户端代码
client, err := rpc.Dial("tcp", "localhost:8080")
if err != nil {
log.Fatal(err)
}
var result Result
err = client.Call("Calculator.Compute", Args{A: 100, B: 25}, &result)
示例 7:错误处理
package main
import (
"bytes"
"encoding/gob"
"fmt"
"io"
"log"
)
// TestData 测试数据
type TestData struct {
A int
B string
}
func main() {
fmt.Println("=== gob 错误处理 ===\n")
// 1. 编码错误 - 不支持的类型
fmt.Println("1. 不支持的类型:")
type BadStruct struct {
Func func() // 函数类型不支持
Chan chan int // 通道类型不支持
}
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(BadStruct{})
if err != nil {
fmt.Printf(" ✗ 编码失败:%v\n", err)
}
// 2. 解码错误 - 数据损坏
fmt.Println("\n2. 数据损坏:")
// 创建有效数据
validData := TestData{A: 42, B: "Hello"}
buf.Reset()
enc.Encode(validData)
// 损坏数据
corrupted := buf.Bytes()
if len(corrupted) > 10 {
corrupted[5] = 0xFF // 修改关键字节
}
var decoded TestData
dec := gob.NewDecoder(bytes.NewReader(corrupted))
err = dec.Decode(&decoded)
if err != nil {
fmt.Printf(" ✗ 解码失败:%v\n", err)
}
// 3. 解码错误 - 空数据
fmt.Println("\n3. 空数据:")
emptyData := []byte{}
dec = gob.NewDecoder(bytes.NewReader(emptyData))
err = dec.Decode(&decoded)
if err != nil {
if err == io.EOF {
fmt.Printf(" ✓ EOF 错误:%v\n", err)
} else {
fmt.Printf(" ✗ 错误:%v\n", err)
}
}
// 4. 解码错误 - 不完整数据
fmt.Println("\n4. 不完整数据:")
buf.Reset()
enc.Encode(validData)
incomplete := buf.Bytes()[:5] // 截断数据
dec = gob.NewDecoder(bytes.NewReader(incomplete))
err = dec.Decode(&decoded)
if err != nil {
fmt.Printf(" ✗ 解码失败:%v\n", err)
}
// 5. 类型不匹配
fmt.Println("\n5. 类型不匹配:")
// 编码为一种类型
buf.Reset()
enc.Encode(42) // int
// 尝试解码为另一种类型
var str string
dec = gob.NewDecoder(bytes.NewReader(buf.Bytes()))
err = dec.Decode(&str)
if err != nil {
fmt.Printf(" ✗ 类型不匹配:%v\n", err)
}
// 6. 接口未注册
fmt.Println("\n6. 接口未注册:")
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
type Container struct {
Shape Shape
}
// 不注册 Circle 类型
container := Container{Shape: Circle{Radius: 5.0}}
buf.Reset()
err = enc.Encode(container)
if err != nil {
fmt.Printf(" ✗ 编码失败(接口未注册): %v\n", err)
}
// 7. 循环引用(会导致 panic)
fmt.Println("\n7. 循环引用:")
type Node struct {
Value int
Next *Node
}
node1 := &Node{Value: 1}
node2 := &Node{Value: 2, Next: node1}
node1.Next = node2 // 创建循环引用
buf.Reset()
defer func() {
if r := recover(); r != nil {
fmt.Printf(" ✓ 检测到循环引用:%v\n", r)
}
}()
// 这会导致 panic
// enc.Encode(node1)
fmt.Printf(" ⚠ 循环引用会导致 panic(已跳过实际编码)\n")
// 8. 正常编码/解码
fmt.Println("\n8. 正常编码/解码:")
buf.Reset()
err = enc.Encode(TestData{A: 100, B: "World"})
if err != nil {
fmt.Printf(" ✗ 编码失败:%v\n", err)
} else {
fmt.Printf(" ✓ 编码成功\n")
var result TestData
dec = gob.NewDecoder(bytes.NewReader(buf.Bytes()))
err = dec.Decode(&result)
if err != nil {
fmt.Printf(" ✗ 解码失败:%v\n", err)
} else {
fmt.Printf(" ✓ 解码成功:A=%d, B=%s\n", result.A, result.B)
}
}
// 9. 多次编码/解码
fmt.Println("\n9. 多次编码/解码:")
buf.Reset()
values := []interface{}{1, "two", 3.0, true}
for _, v := range values {
enc.Encode(v)
}
fmt.Printf(" 编码了 %d 个值\n", len(values))
dec = gob.NewDecoder(bytes.NewReader(buf.Bytes()))
var (
i int
s string
f float64
b bool
)
dec.Decode(&i)
dec.Decode(&s)
dec.Decode(&f)
dec.Decode(&b)
fmt.Printf(" 解码结果:%d, %s, %.1f, %v\n", i, s, f, b)
}
输出:
=== gob 错误处理 ===
1. 不支持的类型:
✗ 编码失败:gob: type not registered but it's not a pointer
2. 数据损坏:
✗ 解码失败:unexpected EOF
3. 空数据:
✓ EOF 错误:EOF
4. 不完整数据:
✗ 解码失败:unexpected EOF
5. 类型不匹配:
✗ 类型不匹配:cannot decode into string
6. 接口未注册:
✗ 编码失败(接口未注册): gob: type not registered but it's not a pointer
7. 循环引用:
⚠ 循环引用会导致 panic(已跳过实际编码)
8. 正常编码/解码:
✓ 编码成功
✓ 解码成功:A=100, B=World
9. 多次编码/解码:
编码了 4 个值
解码结果:1, two, 3.0, true
最佳实践
✅ 推荐做法
-
总是检查错误
// ✅ 推荐 err := enc.Encode(data) if err != nil { return err } err = dec.Decode(&result) if err != nil { return err } -
接口类型必须注册
// ✅ 推荐:在 init 中注册 func init() { gob.Register(Circle{}) gob.Register(Rectangle{}) } -
使用指针提高效率
// ✅ 推荐:编码指针 err := enc.Encode(&largeStruct) // ✅ 推荐:解码到指针 err := dec.Decode(&result) -
只导出需要编码的字段
// ✅ 推荐:大写字段会被编码 type User struct { ID int // ✓ 导出 Name string // ✓ 导出 email string // ✗ 未导出,不会编码 } -
使用版本控制
// ✅ 推荐:添加版本字段 type Data struct { Version int Payload interface{} }
❌ 不安全做法
-
不要编码不支持的类型
// ❌ 错误 type Bad struct { Func func() Chan chan int } // ✅ 正确:只编码支持的类型 type Good struct { Data int Text string } -
不要忽略接口注册
// ❌ 错误 var shape Shape enc.Encode(shape) // 失败 // ✅ 正确 gob.Register(Circle{}) enc.Encode(shape) // 成功 -
不要创建循环引用
// ❌ 错误 node1.Next = node2 node2.Next = node1 // 循环引用 enc.Encode(node1) // panic // ✅ 正确:使用指针或避免循环
性能优化
1. 重用 Encoder/Decoder
// ✅ 推荐:重用编码器
type EncoderPool struct {
enc *gob.Encoder
buf *bytes.Buffer
}
func NewEncoderPool() *EncoderPool {
buf := &bytes.Buffer{}
return &EncoderPool{
enc: gob.NewEncoder(buf),
buf: buf,
}
}
func (p *EncoderPool) Encode(v interface{}) []byte {
p.buf.Reset()
p.enc.Encode(v)
return p.buf.Bytes()
}
2. 预分配缓冲区
// ✅ 推荐:预分配
buf := bytes.NewBuffer(make([]byte, 0, 1024))
enc := gob.NewEncoder(buf)
3. 批量编码
// ✅ 推荐:批量编码
items := []Item{/* ... */}
for _, item := range items {
enc.Encode(item)
}
// ❌ 不推荐:创建多个编码器
for _, item := range items {
buf := &bytes.Buffer{}
enc := gob.NewEncoder(buf)
enc.Encode(item)
}
总结
核心类型
| 类型 | 用途 | 说明 |
|---|---|---|
| Encoder | 编码器 | 将 Go 值编码为 gob |
| Decoder | 解码器 | 从 gob 解码为 Go 值 |
核心函数
| 函数 | 用途 | 说明 |
|---|---|---|
| NewEncoder | 创建编码器 | 需要 io.Writer |
| NewDecoder | 创建解码器 | 需要 io.Reader |
| Register | 注册类型 | 接口类型必须注册 |
| Encode | 编码 | 将值编码为 gob |
| Decode | 解码 | 从 gob 解码为值 |
支持的类型
| 类型 | 支持 | 说明 |
|---|---|---|
| 整数 | ✅ | int, int8-64, uint, uint8-64 |
| 浮点数 | ✅ | float32, float64 |
| 复数 | ✅ | complex64, complex128 |
| 布尔 | ✅ | bool |
| 字符串 | ✅ | string |
| 字节切片 | ✅ | []byte(优化) |
| 结构体 | ✅ | 仅导出字段 |
| 切片 | ✅ | slice |
| 数组 | ✅ | array |
| 映射 | ✅ | map |
| 指针 | ✅ | pointer |
| 接口 | ✅ | 需要注册 |
| 通道 | ❌ | chan |
| 函数 | ❌ | func |
使用场景
| 场景 | 推荐方法 | 说明 |
|---|---|---|
| RPC 通信 | gob + net/rpc | Go 标准 RPC |
| 数据持久化 | Encoder/Decoder + 文件 | 存储到文件 |
| 缓存系统 | Encoder/Decoder + 内存 | 内存缓存 |
| 进程间通信 | Encoder + pipe/socket | 管道/套接字 |
| 接口编码 | Register + Encode | 注册具体类型 |
与其他格式对比
| 特性 | gob | JSON | Protobuf |
|---|---|---|---|
| 可读性 | 不可读 | 可读 | 不可读 |
| 大小 | 小 | 中 | 最小 |
| 性能 | 快 | 中 | 最快 |
| 跨语言 | ❌ | ✅ | ✅ |
| 类型信息 | ✅ | ❌ | ❌ |
| 接口支持 | ✅ | ❌ | ❌ |
参考资料
最后更新:2026-04-03
Go 版本:Go 1.23+