Skip to main content

Serilization

序列化是RPC框架中的关键环节,负责将数据结构转换为可传输的字节流,以及将接收到的字节流还原为数据结构。

序列化接口

fyerrpc通过codec.Codec接口定义了序列化和反序列化的标准操作:

type Codec interface {
// Encode 将对象序列化为字节数组
Encode(v interface{}) ([]byte, error)

// Decode 将字节数组反序列化为对象
Decode(data []byte, v interface{}) error

// Name 返回编解码器的名称
Name() string
}

所有的序列化实现都必须遵循这个接口,确保框架可以统一处理不同的序列化方式。

支持的序列化类型

fyerrpc目前支持两种序列化类型,分别是:

// Type 定义了支持的序列化类型
type Type uint8

const (
JSON Type = iota // JSON序列化
Protobuf // Protobuf序列化
)

在协议层面,这些类型对应的值为:

// 序列化类型
const (
SerializationTypeJSON = uint8(0x01) // JSON序列化
SerializationTypeProtobuf = uint8(0x02) // Protobuf序列化
)

JSON序列化

实现方式

fyerrpc的JSON序列化是通过Go标准库的encoding/json包实现的:

// JsonCodec 实现了 Codec 接口
type JsonCodec struct{}

// Encode 将对象序列化为 JSON 字节数组
func (c *JsonCodec) Encode(v interface{}) ([]byte, error) {
return json.Marshal(v)
}

// Decode 将 JSON 字节数组反序列化为对象
func (c *JsonCodec) Decode(data []byte, v interface{}) error {
return json.Unmarshal(data, v)
}

// Name 返回编解码器的名称
func (c *JsonCodec) Name() string {
return "json"
}

使用方法

在客户端或服务端配置中指定使用JSON序列化:

// 服务端
server := api.NewServer(&api.ServerOptions{
Address: ":8000",
SerializeType: protocol.SerializationTypeJSON, // 使用JSON序列化
})

// 客户端
client, err := api.NewClient(&api.ClientOptions{
Address: "localhost:8000",
SerializeType: protocol.SerializationTypeJSON, // 使用JSON序列化
})

如果使用底层API,可以直接获取JSON编解码器:

import "github.com/fyerfyer/fyer-rpc/protocol/codec"

// 获取JSON编解码器
jsonCodec := codec.GetCodec(codec.JSON)

// 序列化
data, err := jsonCodec.Encode(myStruct)
if err != nil {
log.Fatalf("Failed to encode: %v", err)
}

// 反序列化
var result MyStruct
err = jsonCodec.Decode(data, &result)
if err != nil {
log.Fatalf("Failed to decode: %v", err)
}

Protobuf序列化

实现方式

fyerrpc的Protobuf序列化是通过google.golang.org/protobuf/proto包实现的:

// ProtobufCodec 实现了 Codec 接口
type ProtobufCodec struct{}

// Encode 将对象序列化为 Protobuf 字节数组
func (c *ProtobufCodec) Encode(v interface{}) ([]byte, error) {
// 类型断言确保v是proto.Message类型
if pm, ok := v.(proto.Message); ok {
return proto.Marshal(pm)
}
return nil, ErrInvalidMessage
}

// Decode 将 Protobuf 字节数组反序列化为对象
func (c *ProtobufCodec) Decode(data []byte, v interface{}) error {
// 类型断言确保v是proto.Message类型
if pm, ok := v.(proto.Message); ok {
return proto.Unmarshal(data, pm)
}
return ErrInvalidMessage
}

// Name 返回编解码器的名称
func (c *ProtobufCodec) Name() string {
return "protobuf"
}

使用方法

定义Protocol Buffers

首先,需要创建.proto文件定义消息结构:

syntax = "proto3";
package example;

option go_package = "github.com/fyerfyer/fyer-rpc/example/proto";

message HelloRequest {
string name = 1;
}

message HelloResponse {
string message = 1;
}

然后使用protoc工具生成Go代码:

protoc --go_out=. --go_opt=paths=source_relative hello.proto

在fyerrpc中使用Protobuf

在客户端或服务端配置中指定使用Protobuf序列化:

// 服务端
server := api.NewServer(&api.ServerOptions{
Address: ":8000",
SerializeType: protocol.SerializationTypeProtobuf, // 使用Protobuf序列化
})

// 客户端
client, err := api.NewClient(&api.ClientOptions{
Address: "localhost:8000",
SerializeType: protocol.SerializationTypeProtobuf, // 使用Protobuf序列化
})

如果使用底层API,可以直接获取Protobuf编解码器:

import (
"github.com/fyerfyer/fyer-rpc/protocol/codec"
"github.com/fyerfyer/fyer-rpc/example/proto"
)

// 获取Protobuf编解码器
pbCodec := codec.GetCodec(codec.Protobuf)

// 创建Protobuf消息
request := &proto.HelloRequest{
Name: "World",
}

// 序列化
data, err := pbCodec.Encode(request)
if err != nil {
log.Fatalf("Failed to encode: %v", err)
}

// 反序列化
response := &proto.HelloResponse{}
err = pbCodec.Decode(data, response)
if err != nil {
log.Fatalf("Failed to decode: %v", err)
}

协议中的序列化

fyerrpc协议头中包含了序列化类型字段,用于标识消息体使用的序列化方式:

type Header struct {
MagicNumber uint16 // 魔数,用于校验报文
Version uint8 // 协议版本号
MessageType uint8 // 消息类型(请求/响应)
CompressType uint8 // 压缩类型
SerializationType uint8 // 序列化类型
MessageID uint64 // 消息ID,用于多路复用
MetadataSize uint32 // 元数据长度
PayloadSize uint32 // 消息体长度
}

当接收到消息时,框架会根据头部的SerializationType字段选择合适的解码器:

func GetCodecByType(serializationType uint8) codec.Codec {
switch serializationType {
case SerializationTypeJSON:
return codec.GetCodec(codec.JSON)
case SerializationTypeProtobuf:
return codec.GetCodec(codec.Protobuf)
default:
return nil
}
}

元数据序列化

fyerrpc中的元数据(Metadata)也需要序列化和反序列化:

// Metadata 元数据结构
type Metadata struct {
ServiceName string // 服务名称
MethodName string // 方法名称
Error string // 错误信息(仅响应消息使用)
Extra map[string]string // 额外的元数据,如trace_id等
}

元数据的序列化方式由消息头的SerializationType字段指定,与消息体使用相同的序列化方式。

示例代码

JSON序列化示例

package main

import (
"context"
"fmt"
"log"

"github.com/fyerfyer/fyer-rpc/api"
"github.com/fyerfyer/fyer-rpc/protocol"
)

// 请求和响应结构体
type HelloRequest struct {
Name string `json:"name"`
}

type HelloResponse struct {
Message string `json:"message"`
}

func main() {
// 创建客户端,使用JSON序列化
client, err := api.NewClient(&api.ClientOptions{
Address: "localhost:8000",
SerializeType: protocol.SerializationTypeJSON,
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()

// 创建请求
request := &HelloRequest{Name: "World"}
response := &HelloResponse{}

// 调用远程服务
err = client.Call(context.Background(), "GreeterService", "SayHello", request, response)
if err != nil {
log.Fatalf("RPC call failed: %v", err)
}

fmt.Printf("Response: %s\n", response.Message)
}

Protobuf序列化示例

package main

import (
"context"
"fmt"
"log"

"github.com/fyerfyer/fyer-rpc/api"
"github.com/fyerfyer/fyer-rpc/protocol"
"github.com/fyerfyer/fyer-rpc/example/proto"
)

func main() {
// 创建客户端,使用Protobuf序列化
client, err := api.NewClient(&api.ClientOptions{
Address: "localhost:8000",
SerializeType: protocol.SerializationTypeProtobuf,
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()

// 创建Protobuf请求
request := &proto.HelloRequest{Name: "World"}
response := &proto.HelloResponse{}

// 调用远程服务
err = client.Call(context.Background(), "GreeterService", "SayHello", request, response)
if err != nil {
log.Fatalf("RPC call failed: %v", err)
}

fmt.Printf("Response: %s\n", response.Message)
}