Golang 序列化的方式:

  • Binary
  • Gob
  • JSON
  • Protobuf
一. Binary
// OK
type Message struct {
Id uint64
Size uint64
} // Wrong
/*type Message struct {
Id int
Size int
Data string
}*/ func BinaryRW() {
m1 := Message{1, 1024}
buf := new(bytes.Buffer) if err := binary.Write(buf, binary.LittleEndian, m1); err != nil {
log.Fatal("binary write error:", err)
} var m2 Message
if err := binary.Read(buf, binary.LittleEndian, &m2); err != nil {
log.Fatal("binary read error:", err)
}
}
binary.Write
// Data must be a fixed-size value or a slice of fixed-size values.
func Write(w io.Writer, order ByteOrder, data interface{}) error {

解决办法:

  • int 换成 int32 等固定大小的类型
  • slice 换成类似 [8]byte 这种固定大小
  • 选择其他序列化方式
二. Gob

针对 binary 不能直接使用 string 和 slice 问题,可以使用 gob。

type Message2 struct {
Id uint64
Size uint64
Data string
} func GobEncodeDecode() {
m1 := Message2{2, 1024, "gob"}
var buf bytes.Buffer enc := gob.NewEncoder(&buf)
dec := gob.NewDecoder(&buf) if err := enc.Encode(m1); err != nil {
log.Fatal("encode error:", err)
} var m2 Message2
if err := dec.Decode(&m2); err != nil {
log.Fatal("decode error:", err)
}
}
三. JSON

还可以使用 json 传递数据

type Message2 struct {
Id uint64 `json:"id"`
Size uint64 `json:"size"`
Data string `json:"data"`
} func JsonEncodeDecode() {
m1 := Message2{3, 1024, "json"}
var buf []byte
var err error if buf, err = json.Marshal(m1); err != nil {
log.Fatal("json marshal error:", err)
} var m2 Message2
if err = json.Unmarshal(buf, &m2); err != nil {
log.Fatal("json unmarshal error:", err)
}
}
四. Protobuf

当然,还可以使用 protobuf 来序列化.

test.proto

syntax = "proto2";
package example; message Message {
required uint64 id = 1;
required uint64 size = 2;
required string data = 3;
}
func ProtoEncodeDecode() {
m1 := &example.Message{
Id: proto.Uint64(4),
Size: proto.Uint64(1024),
Data: proto.String("proto"),
} buf, err := proto.Marshal(m1)
if err != nil {
log.Fatal("proto marshal error:", err)
} var m2 example.Message
if err = proto.Unmarshal(buf, &m2); err != nil {
log.Fatal("proto unmarshal error:", err)
}
fmt.Println(m2.GetId(), m2.GetSize(), m2.GetData())
}
五. BenchMark 对比

现在来对比下这几种序列化方式的性能。

目录结构:

$ tree serialize
serialize
├── serialize.go
├── serialize_test.go
└── example
├── test.pb.go
└── test.proto

serialize.go

package serialize

import (
"bytes"
"encoding/binary"
"encoding/gob"
"encoding/json"
"log"
"serialize/example" "github.com/golang/protobuf/proto"
) type Message struct {
Id uint64
Size uint64
} type Message2 struct {
Id uint64 `json:"id"`
Size uint64 `json:"size"`
Data string `json:"data"`
} func BinaryRW() {
m1 := Message{1, 1024}
buf := new(bytes.Buffer) if err := binary.Write(buf, binary.LittleEndian, m1); err != nil {
log.Fatal("binary write error:", err)
} var m2 Message
if err := binary.Read(buf, binary.LittleEndian, &m2); err != nil {
log.Fatal("binary read error:", err)
}
} func GobEncodeDecode() {
m1 := Message2{2, 1024, "gob"}
var buf bytes.Buffer enc := gob.NewEncoder(&buf)
dec := gob.NewDecoder(&buf) if err := enc.Encode(m1); err != nil {
log.Fatal("encode error:", err)
} var m2 Message2
if err := dec.Decode(&m2); err != nil {
log.Fatal("decode error:", err)
}
} func JsonEncodeDecode() {
m1 := Message2{3, 1024, "json"}
var buf []byte
var err error if buf, err = json.Marshal(m1); err != nil {
log.Fatal("json marshal error:", err)
} var m2 Message2
if err = json.Unmarshal(buf, &m2); err != nil {
log.Fatal("json unmarshal error:", err)
}
} func ProtoEncodeDecode() {
m1 := &example.Message{
Id: proto.Uint64(4),
Size: proto.Uint64(1024),
Data: proto.String("proto"),
} buf, err := proto.Marshal(m1)
if err != nil {
log.Fatal("proto marshal error:", err)
} var m2 example.Message
if err = proto.Unmarshal(buf, &m2); err != nil {
log.Fatal("proto unmarshal error:", err)
}
}

serialize_test.go

package serialize

import (
"testing"
) func BenchmarkBinaryRW(b *testing.B) {
for i := 0; i < b.N; i++ {
BinaryRW()
}
} func BenchmarkGobEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
GobEncodeDecode()
}
} func BenchmarkJsonEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
JsonEncodeDecode()
}
} func BenchmarkProtoEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
ProtoEncodeDecode()
}
}
BenchmarkBinaryRW-4              2000000           609 ns/op
BenchmarkGobEncodeDecode-4 100000 23689 ns/op
BenchmarkJsonEncodeDecode-4 1000000 1889 ns/op
BenchmarkProtoEncodeDecode-4 2000000 778 ns/op
PASS
ok serialize 8.722s

写在最后