grpc笔记

什么是.proto文件

protocol buf是适应grpc框架的IDL(Interface descripition language)语言,使用其编写的文件后缀为.proto

如何编译.proto文件

  • 使用protoc编译器
    • protoc编译器是专门用来将proto文件转化为c++动态库的编译器,它将每个proto文件转化为后缀为.pb.h的头文件和后缀为.pb.cc的实现文件
    • protoc编译器的常用选项包括:
      • –proto_path : 指定proto文件的路径
      • –cpp_out : 指定.h和.cc文件的生成路径
  • 使用cmake生成protoc命令
    • cmake提供了FindProtobuf模块,可以通过find_package命令查找Protobuf进行使用
    • protobuf_generate_cpp : 该命令将自定义命令添加到处理.proto文件的C++代码中,语法如下:
      protobuf_generate_cpp (<SRCS> <HDRS> [DESCRIPTORS <DESC>] [EXPORT_MACRO <MACRO>] [<ARGN>...])
      SRCSHDRS 均为自定义变量,用于定义生成的源文件或头文件, ARGN 为要添加的.proto文件,使用示例如下:
      protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS foo.proto)
      add_executable(bar bar.cc ${PROTO_SRCS} ${PROTO_HDRS})

      protobuf_generate_cpp 和 protobuf_generate_python 函数以及 add_executable() 或 add_library() 调用只能在同一目录中正常工作。

    • protobuf_generate : 官方缺少该命令的实际使用文档,但是已经有人自己向官方提交了请求
    • protocol buffer内置的cpp code generator只能生成普通消息类型的头文件和源文件,需要另外的插件来处理grpc客户端和服务端的代码生成

.proto文件各类别(type)含义

首先感谢李文周先生对protocol buffer v3 官方文档的翻译

  • package

    设置当前proto文件的命名空间

  • field(字段)

    字段由字段标签/消息字段,字段类型,字段名和字段编号组成,使用message foo生成的set_foo()可以设置字段值,使用foo()可以获取字段值

    字段标签

    • option : 开启或者关闭某选项, 作用域限制在message类中
    • repeated :作为消息字段时表示生成类似于vector<foo>的数组
    • optional : 作为消息字段时可以用于追踪是否设置该字段。

      一般情况下(即未指定optional时)若未设置字段或者将字段值设为默认值时编译器不会将其序列化,反序列化时虽然缺少字段但不会报错,在反序列化后即使没有设置该字段使用foo()仍会获得默认值,这样就会无法判断是否在另一端设置了该字段,这种情况叫做implicit presence,可以通过设置optional来解决问题,使用optional后,只要设置了该字段,无论是否为默认值,编译器都会序列化该字段,并且还会生成 has_foo() 函数来检查是否设置了该字段

    字段类型

    • int32 :32位整数,使用变长编码(Varint),这种编码方式对于小的数字非常高效,但对于大的数字或负数则不然。因为Varint编码方式是将整数编码为一个或多个字节,每个字节的最高位用于表示是否还有更多的字节。如果最高位是 0,那么这就是最后一个(或唯一的)字节。如果最高位是 1,那么表示后面还有更多的字节。
    • sint32 : 如果你的数据中包含很多负数,你可能会想使用 sint32 类型,它对负数进行了优化。sint32 类型使用的是 ZigZag 编码,这种编码方式将负数映射到正整数,使得 -1 映射为 1-2 映射为 3,以此类推。这样,小的负数就可以用较少的字节表示,提高了编码效率。
    • enum : 不是基本数据类型,设置枚举类型,第一个值必须被设置为0

官方文档关于field的详细介绍: 🔗️

  • service

    给出以下定义:
    1
    2
    3
    service Foo {
    rpc Bar(FooRequest) returns(FooResponse);
    }
    protoc会生成Foo类并生成其内定义的虚函数,如代码所示,protoc会生成一个名为Bar的虚函数,它接受FooRequest类型的参数并返回FooResponse类型的结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
      virtual void Bar(RpcController* controller, const FooRequest* request, FooResponse* response, Closure* done);
    ```
    RpcController类用来**调停**一个函数调用,它的主要目的是用来是提供一种方法来操作特定于RPC实现的设置并找出RPC级别的错误。比如其可以调用setfailed()方法,在客户端调用failed()方法
    > failed()如果RPC调用失败,则返回true。失败的可能原因取决于RPC实现。failed()大多数情况下只能在客户端调用,并且在调用完成之前不得调用。setfailed()导致failed()在客户端返回true。reason将被合并到返回的消息中errorText()。如果您发现需要返回有关失败的机器可读信息,则应将其合并到响应协议缓冲区中,并且不应调用setFailed().

    Foo类继承自Service类

    ## protobuf生成的c++代码
    - 每个message字段都会生成字段名类并且该类继承自`google::protobuf::Message`父类,该父类不包括纯虚函数并且子类默认情况下会重写所有父类虚函数,该父类接口定义了以下函数:
    - `bool ParseFromString(const string& data)` : 反序列化二进制字符串data,注意此时data是以引用形式传入
    - `bool SerializeToString(string* output) const` : 从output指向的字符串中序列化字符串,注意此时output是以指针形式传入
    - 同时子类还实现了以下函数:
    - `const UnknownFieldSet& unknown_fields() const` : 返回解析字段时发现的未知字段集
    - 对于以下字段定义:
    ```proto
    optional string foo = 1;
    required string foo = 1;
    optional bytes foo = 1;
    required bytes foo = 1;
    除了set_foo, foo, has_foo等成员函数外,还会生成以下成员函数:
    • string* mutable_foo() : 返回指向存储字段值的可变字符串对象的指针。由于foo()函数返回foo值的常量引用,所以使用该函数可以修改foo的值
    • set_allocated_foo(string* fo) : 将fo指向的字符串设为字段的值,并清除之前设置过的值,若fo为空,则相当于调用了clear_foo来取消设置字段

      对numeric,enum,embedded message 生成的函数类似

  • 对于以下字段定义:
    1
    repeated int32 foo = 1;
    生成的函数还包括:
    • int foo_size() : 返回repeated列表中元素的数量
    • int32 foo(int index) const : 返回列表中索引为index的元素的值
    • void add_foo(int32 value) : 类似于vector的push_back方法

      对string,enum,embedded message 生成的函数类似

rpc

refletcion

在gRPC中,Reflection是一种服务,它使远程客户端能够查询服务器以获取有关其公开服务的信息。这些信息包括服务名称、方法(包括其输入/输出类型)等。这使得客户端可以动态地(在运行时)理解和调用服务,而无需在编译时知道服务的确切定义。

metadata

元数据是与RPC相关的客户端和服务器之间提供信息的侧通道。gRPC元数据是一对键值对数据,与初始或最终的gRPC请求或响应一起发送。它用于提供有关调用的附加信息,如身份验证凭据、跟踪信息或自定义标头。gRPC元数据使用HTTP/2标头实现,键是ASCII字符串,值可以是ASCII字符串或二进制数据。元数据可用于身份验证、跟踪和自定义标头。


grpc笔记
http://example.com/2023/08/26/grpc笔记/
作者
李凯华
发布于
2023年8月26日
许可协议