Proto | 青训营笔记

169 阅读5分钟

这是我参与「第五届青训营 」笔记创作活动的第 7 天

一、本堂课主要内容

  • 安装并配置proto环境
  • 了解proto语法
  • 学会通过protoc生成代码

二、详细知识点介绍

Proto

安装ProtoBuf

1、下载protocol buffers Releases · protocolbuffers/protobuf (github.com)

  • Protocol buffers,通常称为 Protobuf,是Google开发的一种协议,可以对结构数据进行序列化和反序列化操作,在网络通信中很有用
  • 我们下载对应操作系统的zip文件,解压,并将bin目录配置到环境变量中即可
  • 最后在cmd或bash中输入protoc命令查看是否安装并配置成功

2、安装gRPC核心库

go get google.golang.org/grpc

3、安装protocol编译器。它开源生成各种不同语言的代码。因此,除了这个编译器,我们还需要配合各个语言的代码生成工具。对于Golang来说,这个工具是protoc-gen-go

这里有个小坑,github.com/golang/protobuf/protoc-gen-gogoogle.golang.org/protobuf/cmd/protoc-gen-go是不同的!前者是旧版本,后者是Google接管的新版本,他们的API是不同的,用于生成的命令,生成的文件都是不一样的。由于目前的gRPC-go源码中的example是使用后者的生成方式,所以我们也采用google版本。

下面我们通过命令安装两个库(因为这些文件在安装grpc时以及下载了,这里只需要install即可)

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Proto文件编写

下面简单写一个小demo,新建两个文件夹,分别作为客户端和服务端。

image-20230120174308269

proto文件内容如下(可以当作模板记下来)

// 使用proto3语法
syntax = "proto3";
​
// 生成的go文件处于哪个目录哪个包中
// 这里声称在当前目录,service包中
option go_package = ".;service";
​
// 我们需要定义一个服务,在服务中需要有一个方法,这个方法可以接收客户端参数,返回服务端响应
// 其实很容易可以看出,我们定义了一个service,称为SayHello,这个服务有一个rpc方法,名为SayHello
// 这个方法会发送一个HelloRequest返回一个HelloResponse
service SayHello {
  rpc SayHello(HelloRequest) returns (HelloResponse) {}
}
​
// message关键字,可以理解为结构体
// 这个比较特别的是变量后的"赋值"(这里并不是赋值,而是定义这个变量在message中的位置)
message HelloRequest {
  string requestName = 1;
//  int64 age = 2;
}
​
message HelloResponse {
  string responseMsg = 1;
}

接下来可以通过protoc生成对应语言的代码,打开Terminal,进入proto目录,输入一些代码即可

两个命令(当作模板可以记下)

protoc --go_out=. hello.proto
protoc --go-grpc_out=. hello.proto

输入完可以发现在proto目录下生成了两个文件,我们使用时只需要重写或修改其中的我们定义的方法,加上业务逻辑即可

Proto文件介绍

message

message: protobuf 中一个定义消息类型是通过关键字 message 指定的。消息就是需要传输的数据格式的定义

message 关键字类似于C++/Java中的class,C/Go中的struct

在消息中承载的数据分别对应每个字段,其中每个字段都有一个名字和一种类型

一个proto文件可以定义多个消息类型

字段规则

  • required: 消息体中必填字段。不设置会导致编码异常。在protobuf2中使用,在protobuf3中被删去。
  • optional: 消息体中可选字段。protobuf3中没有了reuired,optional等说明关键字,都默认为optional。
  • repeated: 消息体中可重复字段。重复的值的顺序会被保留,在go中重复的会被定义为切片。

消息号

在消息体的定义中,每个字段都必须要有一个唯一的标识号,标识号是[1, 2^29-1]范围内的一个整数。

形式看上去与"赋值"类似。

嵌套消息

可以在其他消息类型中定义、使用消息类型,在下面的例子中,person消息就定义在PersonInfo消息内

message PersonInfo{
    message Person{
        string name = 1;
        int32 height = 2;
        repeated int32 weight = 3;
    }
    repeated Person info = 1;
}

如果要在父消息外重用这个消息类型,需要使用PersonInfo.Person的形式来使用它,如:

message PersonMessage{
    PersonInfo.Person info = 1;
}

服务定义

如果要将消息类型用在RPC系统,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。

service SearchService{
    # rpc 服务函数名 (参数) 返回 (返回参数)
    rpc Search(SearchRequest) returns (RequestResponse)
}

服务端编写

  • 创建gRPC Server对象,你可以理解为Server端的抽象对象

  • 将 server (其包含需要被调用的服务端接口) 注册到 gRPC Server 的内部注册中心

    这样可以在接受到请求时,通过内部的服务发现,发现该服务端接口并转接进行逻辑处理

  • 创建 Listen,监听TCP端口

  • gRPC Server 开始 lis.Accept,直到 Stop

下面给出以之前写的Hello服务为例,实现以下服务端的编写

// 实现生成代码中未实现的服务
// hello Server
type server struct {
    pb.UnimplementedSayHelloServer
}
​
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
    return &pb.HelloResponse{ResponseMsg: "hello" + req.RequestName}, nil
}

三、实践练习例子

实践的部分代码也已经贴在上面了,以及这里是GitHub

四、课后个人总结

其实我对于gRPC的学习已经基本快速入门了! (笔记只上传一点才不是要水笔记呢!)

这几天没课的时候,将会连载gRPC以及有关网络相关的文章~

除了字节官方推荐的Kitex,gRPC或许能成为你的选择~

五、引用参考

gRPC官网

gRPC 官方文档中文版_V1.0 (oschina.net)

GitHub - protocolbuffers/protobuf: Protocol Buffers - Google's data interchange format