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"
相关推荐
不会编程的懒洋洋1 小时前
Spring Cloud Eureka 服务注册与发现
java·笔记·后端·学习·spring·spring cloud·eureka
NiNg_1_2341 小时前
SpringSecurity入门
后端·spring·springboot·springsecurity
Lucifer三思而后行2 小时前
YashanDB YAC 入门指南与技术详解
数据库·后端
王二端茶倒水3 小时前
大龄程序员兼职跑外卖第五周之亲身感悟
前端·后端·程序员
夜色呦4 小时前
现代电商解决方案:Spring Boot框架实践
数据库·spring boot·后端
爱敲代码的小冰4 小时前
spring boot 请求
java·spring boot·后端
java小吕布5 小时前
Java中的排序算法:探索与比较
java·后端·算法·排序算法
Goboy6 小时前
工欲善其事,必先利其器;小白入门Hadoop必备过程
后端·程序员
李少兄6 小时前
解决 Spring Boot 中 `Ambiguous mapping. Cannot map ‘xxxController‘ method` 错误
java·spring boot·后端
代码小鑫7 小时前
A031-基于SpringBoot的健身房管理系统设计与实现
java·开发语言·数据库·spring boot·后端