rpc,grpc,Protobuf入门

对应视频教程在哔哩哔哩

复制代码
https://space.bilibili.com/523248241

一、先看故事:餐厅点餐

假设你要开一家连锁餐厅(分布式系统),总店(服务提供方)和分店(服务调用方)不在一个地方。

  • RPC(远程调用思想) :分店想用总店的"计算优惠价"功能,就像打电话问总店------拨号、问问题、等回答,跟问身边员工一样简单。

  • gRPC(框架) :你选了顺丰同城专送作为通信方式,速度快、能实时回传进度(流式)。

  • Protobuf(数据格式) :你把菜单和价格写在标准模板纸 上(.proto文件),总店和分店都用这个模板,省去了口头复述的麻烦和错误(二进制高效传输)。

  • 注册中心(Nacos) :总店开张时,去**外卖平台(注册中心)**登记地址和电话。分店要下单时,先去外卖平台查"最近的总店在哪"(服务发现)。

  • 熔断(Sentinel) :如果某家总店今天厨师生病出餐极慢,分店直接不再给这家店下单(熔断),防止整个午餐配送瘫痪。

二、 需要安装的内容

1.Protocol Buffers 编译器protoc

复制代码
https://github.com/protocolbuffers/protobuf/releases

Windows: 从官网下载protoc.exe放到PATH目录

验证:protoc --version

C:\Users\Administrator>protoc --version
libprotoc 35.1

2.Go 插件(生成 gRPC 代码用)

复制代码
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

验证:获取GOPATH路径 你刚刚的安装就是在GOPATH\bin目录下,去查看你的安装exe文件是否存在,同时把GOPATH\bin的目录放入系统环境变量中

复制代码
go  env GOPATH

验证版本命令:

protoc-gen-go-grpc --version

显示内容:

protoc-gen-go-grpc 1.6.2

3.三个插件的关系

复制代码
三者各自定位
1. protoc(核心编译器,本体)
Protocol Buffers 官方命令行编译器,独立二进制程序。
作用:解析 .proto 协议文件,完成语法校验、依赖解析、类型检查,本身不生成任何语言代码,只负责调度插件生成代码。
语法模板:
bash
运行
protoc --插件名=输出目录 源文件.proto
2. protoc-gen-go(Go 基础代码生成插件)
protoc 的Go 代码生成插件,专门生成 protobuf 序列化结构体代码(.pb.go)。
职责:
根据 proto message/enum/service 生成 Go 结构体
实现 proto.Message 接口、序列化 / 反序列化、JSON 转换、字段 getter/setter
只处理普通 proto 数据结构,不生成 gRPC 服务代码
3. protoc-gen-go-grpc(gRPC Go 代码生成插件)
protoc 的gRPC 专用插件,配套 gRPC Go 使用,生成 gRPC 服务端 / 客户端存根代码(_grpc.pb.go)。
职责:
识别 proto 里的 service + rpc 定义
生成服务端接口、客户端调用方法、流式请求 / 响应封装
依赖 protoc-gen-go 生成的基础 .pb.go,不能单独使用
二、层级依赖关系
plaintext
.proto 文件
     ↓
protoc(调度器)
├─→ protoc-gen-go → xxx.pb.go 数据结构体
└─→ protoc-gen-go-grpc → xxx_grpc.pb.go gRPC 服务/客户端
protoc 是总入口,必须先装;
写普通 proto(仅数据结构):只需要 protoc + protoc-gen-go;
写 gRPC 微服务(含 service/rpc):三者缺一不可;
protoc-gen-go-grpc 强依赖 protoc-gen-go 产出的代码,顺序无强制,但必须同时生成。

三、简单示例(golang代码实现)

3.1.项目依赖

复制代码
go mod init mygrpc  # 初始化模块
go get google.golang.org/grpc
go get google.golang.org/protobuf

3.2项目结构

3.3代码内容

1. calculator.proto

复制代码
syntax = "proto3";

package calculator;

//option go_package = "输出目录;Go代码包名";

option go_package = "./;calculator";

service Calculator {
  rpc Add (AddRequest) returns (AddResponse);
}

message AddRequest {
  int32 a = 1;
  int32 b = 2;
}

message AddResponse {
  int32 result = 1;
}

2. 生成代码(在项目根目录执行)

复制代码
protoc --go_out=. --go-grpc_out=. calculator.proto

执行后会自动生成 calculator.pb.gocalculator_grpc.pb.go

运行的命令的注释:

复制代码
1. 基础主体 protoc
Protocol Buffers 官方编译器,用来把 .proto 协议文件编译生成各语言代码。
2. 参数拆解
① --go_out=.
--go_out:生成 protobuf 基础 Go 代码 的插件参数(结构体、序列化、反序列化、消息定义)
.:输出目录为当前文件夹
作用:根据 proto 里的 message 消息,生成 xxx.pb.go 文件。
② --go-grpc_out=.
--go-grpc_out:生成 gRPC Go 服务代码 的插件参数(服务接口、客户端、服务端存根)
.:输出到当前目录
作用:根据 proto 里的 service 服务定义,生成 xxx_grpc.pb.go 文件。
③ calculator.proto
输入源文件:要编译的 protobuf 协议文件,里面定义了计算器的消息和 gRPC 服务。
3. 执行后会生成两个文件
calculator.pb.go:纯 protobuf 消息序列化代码
calculator_grpc.pb.go:gRPC Client、Server 接口代码

备注:需要关注"calculator.proto"文件中的 "option go_package = "输出目录;Go代码包名";"的选项,特别是他的输出目录决定最终生成文件的目录下。

3. server.go

复制代码
package main

import (
	"context"
	"log"
	"net"

	pb "mygrpc" // 导入生成的代码

	"google.golang.org/grpc"
)

// 服务端实现
type server struct {
	pb.UnimplementedCalculatorServer
}

// 实现 Add 方法

func (s *server) Add(ctx context.Context, req *pb.AddRequest) (*pb.AddResponse, error) {
	log.Printf("收到请求: %d + %d", req.A, req.B)
	return &pb.AddResponse{Result: req.A + req.B}, nil
}

func main() {
	// 监听端口
	lis, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalf("监听失败: %v", err)
	}

	// 创建 gRPC 服务器
	s := grpc.NewServer()
	pb.RegisterCalculatorServer(s, &server{})

	log.Println("✅ 服务端启动成功,监听端口 :8080")
	if err := s.Serve(lis); err != nil {
		log.Fatalf("启动失败: %v", err)
	}
}

4.client代码

复制代码
package main

import (
	"context"
	"log"
	"time"

	pb "mygrpc"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func main() {
	// 连接服务端
	conn, err := grpc.NewClient("localhost:8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("连接失败: %v", err)
	}
	defer conn.Close()

	// 创建客户端
	client := pb.NewCalculatorClient(conn)

	// 调用 Add 方法
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	resp, err := client.Add(ctx, &pb.AddRequest{A: 10, B: 20})
	if err != nil {
		log.Fatalf("调用失败: %v", err)
	}

	log.Printf("✅ 计算结果: 10 + 20 = %d", resp.Result)
}

5.代码运行

1.首先在根目录下 go mod tidy

2.go run server/server.go

3.go run client/client.go

4.客户端显示内容:

  1. ✅ 计算结果: 10 + 20 = 30

就是完成内容;