Go中使用 Protobuf 初体验

什么是 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-goprotoc-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.gouser_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)
}

运行步骤

运行服务器

  1. 打开一个终端窗口。
  2. 导航到 your_project/server 目录。
  3. 运行以下命令:
sh 复制代码
go run server.go

你应该会看到服务器正在监听端口 50051。

运行客户端

  1. 打开另一个终端窗口。
  2. 导航到 your_project/client 目录。
  3. 运行以下命令:
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"
相关推荐
ZHOUPUYU10 分钟前
PHP 开发实战:从零搭建一个高性能的 RESTful API 服务
运维·开发语言·后端·html·php
身如柳絮随风扬11 分钟前
除了 JWT,你还用过哪些认证方案?Spring Security 中如何集成 JWT?
java·后端·spring
techdashen16 分钟前
Rust 能帮你捕获什么,又不能捕获什么
开发语言·后端·rust
YOU OU16 分钟前
Spring MVC 练习项目
java·后端·spring
Spider Cat 蜘蛛猫10 小时前
Springboot SSO系统设计文档
java·spring boot·后端
weixin_4217252610 小时前
Linux 编程语言全解析:C、C++、Python、Go、Rust 谁更强?
linux·python·go·c·编程语言
zyk_computer11 小时前
AI 时代,或许 Rust 比 Python 更合适
人工智能·后端·python·ai·rust·ai编程·vibe coding
雨辰AI11 小时前
SpringBoot3 项目国产化改造完整流程|从 MySQL 到人大金仓落地
java·数据库·后端·mysql·政务
GreenTea13 小时前
【Rust 2026教程:从零构建 Mini-OLAP 引擎】第 6 章 Benchmark 与优化路线图
后端
Rust语言中文社区13 小时前
【Rust日报】2026-05-14 Pyrefly v1.0 正式发布:快速的 Python 类型检查器和语言服务器
开发语言·后端·python·rust