什么是 Protobuf
Protocol Buffers(简称 Protobuf)是由 Google 开发的一种轻便高效的结构化数据存储格式,用于在不同语言之间序列化和反序列化数据。它被广泛应用于 RPC(远程过程调用)系统和数据存储系统中。
Protobuf 的核心理念是定义 .proto
文件,这个文件描述了数据结构。通过编译 .proto
文件,可以生成不同编程语言的代码,用于读写这些数据结构。
Protobuf 的优势
- 高效:二进制格式比文本格式(如 JSON、XML)更小、更快。
- 跨语言:支持多种编程语言,如 C++、Java、Python、Go 等。
- 兼容性:支持向后兼容,可以在不影响旧代码的情况下添加新的字段。
项目结构和实操例子
以下是一个简单的 Protobuf 和 Go 的实操例子,展示了如何定义、编译和使用 Protobuf。
项目结构
go
your_project/
├── proto/
│ └── user.proto
├── server/
│ └── server.go
├── client/
│ └── client.go
└── go.mod
步骤 1: 创建项目结构
sh
mkdir -p your_project/{proto,server,client}
cd your_project
go mod init example.com/your_project
步骤 2: 定义 user.proto
文件
在 your_project/proto
目录中创建 user.proto
文件:
go
syntax = "proto3";
package user;
option go_package = "./proto";
service UserService {
rpc GetUser (GetUserRequest) returns (GetUserResponse);
}
message GetUserRequest {
int32 id = 1;
}
message GetUserResponse {
User user = 1;
}
message User {
int32 id = 1;
string name = 2;
string email = 3;
}
步骤 3: 安装 Protobuf 编译器和插件
确保你已经安装了 Protobuf 编译器 protoc
。你可以从 Protobuf Releases 页面下载相应的版本。
然后安装 protoc-gen-go
和 protoc-gen-go-grpc
插件:
sh
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
步骤 4: 编译 Protobuf 文件
在 your_project
目录下运行以下命令生成 Go 代码:
sh
protoc --go_out=. --go-grpc_out=. proto/user.proto
这会在 proto
目录中生成 user.pb.go
和 user_grpc.pb.go
文件。
步骤 5: 实现服务器
在 your_project/server
目录中创建 server.go
文件:
go
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "example.com/your_project/proto"
)
type server struct {
pb.UnimplementedUserServiceServer
}
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
user := &pb.User{
Id: req.Id,
Name: "John Doe",
Email: "johndoe@example.com",
}
return &pb.GetUserResponse{User: user}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
pb.RegisterUserServiceServer(grpcServer, &server{})
log.Println("Server is running on port 50051")
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
步骤 6: 实现客户端
在 your_project/client
目录中创建 client.go
文件:
go
package main
import (
"context"
"log"
"time"
"google.golang.org/grpc"
pb "example.com/your_project/proto"
)
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := pb.NewUserServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
req := &pb.GetUserRequest{Id: 1}
res, err := client.GetUser(ctx, req)
if err != nil {
log.Fatalf("could not get user: %v", err)
}
log.Printf("User: %v", res.User)
}
运行步骤
运行服务器
- 打开一个终端窗口。
- 导航到
your_project/server
目录。 - 运行以下命令:
sh
go run server.go
你应该会看到服务器正在监听端口 50051。
运行客户端
- 打开另一个终端窗口。
- 导航到
your_project/client
目录。 - 运行以下命令:
sh
go run client.go
你应该会看到客户端向服务器发送请求,并接收并打印服务器返回的用户信息。
通过以上步骤,你将了解如何使用 Protobuf 定义数据结构,并通过 gRPC 实现一个简单的客户端-服务器应用程序。
例子 2:序列化
在 example.com/your_project/proto 新建 example.go:
go
syntax = "proto3";
package example;
option go_package = "./proto";
// 定义一个简单的消息类型
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
在 example.com/your_project/ 下运行
sh
protoc --go_out=. --go-grpc_out=. proto/example.proto
在 example.com/your_project/ 新建 main.go
go
package main
import (
"fmt"
"log"
pb "github.com/chengxuyuanermao/test/proto"
"google.golang.org/protobuf/proto"
)
func main() {
// 创建一个新的 Person 消息
p := &pb.Person{
Name: "John Doe",
Id: 1234,
Email: "johndoe@example.com",
}
// 序列化消息到二进制格式
data, err := proto.Marshal(p)
if err != nil {
log.Fatalf("Marshaling error: %v", err)
}
// 打印序列化的二进制数据
fmt.Printf("Serialized data: %x\n", data)
// 反序列化二进制数据到消息
newPerson := &pb.Person{}
err = proto.Unmarshal(data, newPerson)
if err != nil {
log.Fatalf("Unmarshaling error: %v", err)
}
// 打印反序列化的消息
fmt.Printf("Unmarshaled data: %v\n", newPerson)
}
运行 main.go:
sh
Serialized data: 0a084a6f686e20446f6510d2091a136a6f686e646f65406578616d706c652e636f6d
Unmarshaled data: name:"John Doe" id:1234 email:"johndoe@example.com"