gRPC整理

背景

RPC

RPC是远程过程调用(Remote Procedure Call)的缩写。它是一种计算机通信协议,使得程序可以请求另一个进程或者计算机上的服务,就像调用本地的函数一样,从而实现分布式系统之间的交互和通讯。RPC可以大大简化分布式系统的开发,提高系统的可维护性和可扩展性。一个比较常见的用法,RPC下载器,例如:aria2、Motrix,可以用来帮助下载一些限速网盘的资源。

优点

  1. 抽象屏蔽:RPC框架可以屏蔽底层的网络通信细节,使得远程调用就像本地调用一样简单。

  2. 可扩展性:RPC框架可以支持多种协议和编码方式,可以适应不同场景的需求,同时也可以方便地添加新的功能和服务。

  3. 可靠性:RPC框架通常会提供各种机制来保证通信的可靠性,如超时重试、错误处理等。

  4. 高效性:RPC框架通常使用二进制协议和高效的序列化方式,可以大大减少网络传输的数据量,提高系统的性能。

  5. 语言无关性:RPC框架可以支持多种编程语言,使得不同语言的程序可以方便地进行交互和通讯。

缺点

  1. 依赖网络:RPC需要通过网络进行通信,因此对网络的稳定性和延迟要求比较高。

  2. 难以调试:由于RPC是跨进程或者跨计算机的调用,因此调试起来比较困难,需要使用一些特殊的工具和技术。

  3. 数据格式限制:RPC框架通常会限制数据的格式和大小,如果需要传输大量的数据或者复杂的数据结构,可能会导致性能问题。

  4. 安全性问题:RPC通常不会提供加密和认证等安全机制,需要在应用层进行处理,否则容易受到攻击。

  5. 可靠性问题:RPC框架虽然提供了一些机制来保证通信的可靠性,但仍然可能出现通信失败、丢失消息等情况,需要应用程序自己处理。

gRPC

概括

gRPC是Google开源的一种高性能、通用的远程过程调用(RPC)框架,基于Protocol Buffers序列化协议进行数据传输。

优点

  1. 高性能:gRPC采用基于HTTP/2的二进制传输协议,可以实现双向流、头部压缩和多路复用等特性,提高了网络传输的效率和性能。

  2. 多语言支持:gRPC支持多种编程语言,包括C++、Java、Python、Go、Ruby等,可以方便地构建跨语言的分布式系统。

  3. 自动生成代码:gRPC可以根据服务定义文件自动生成客户端和服务器端的代码,大大简化了开发过程。

  4. 可扩展性:gRPC支持多种负载均衡算法和服务发现机制,可以适应不同场景的需求。

  5. 安全性:gRPC支持TLS加密和认证等安全机制,保障通信的安全性。

  6. 易于使用和维护:gRPC提供了丰富的文档和工具链,使得开发和维护分布式系统变得更加容易。

  7. 支持多种序列化协议:gRPC支持多种序列化协议,包括Google开发的Protocol Buffers序列化协议和JSON等,可以根据实际需求选择最适合的序列化方式。

  8. 支持流式数据传输:gRPC支持双向流、客户端流和服务器端流等多种流式数据传输方式,可以满足不同的业务需求。

缺点

  1. 不支持RESTful API:gRPC不支持基于HTTP的RESTful API,无法与现有的RESTful API进行兼容和集成。

  2. 不支持浏览器端:gRPC目前不支持Web浏览器端,因为浏览器不支持HTTP/2协议。

  3. 依赖Protocol Buffers:gRPC默认使用Google开发的Protocol Buffers序列化协议,如果需要使用其他的序列化协议,则需要自行实现。

  4. 难以调试:由于gRPC采用二进制协议,数据的传输和解析都是以二进制形式进行的,对于调试和排错带来了一定的困难。

  5. 安全性依赖于TLS:虽然gRPC支持TLS加密和认证等安全机制,但这些机制都依赖于TLS协议,如果TLS协议本身存在漏洞或被攻击,则会影响gRPC的安全性。

Protobuf介绍

概括

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

proto2

 // 表示正在使用proto2命令
 syntax = "proto2"; 
 //包声明 test 
 //也可以声明为二级类型,例如a.b,表示a类别下b子类别
 //如果另一个文件引这个包,要用这里的东西,需要使用test.xxx
 package test; 
 //每句话都要加分号结尾,import要带上.proto
 //引一个没用的包,会警告,不报错
 import "A.proto";
 import "google/protobuf/empty.proto";
 //给go语言用的,如果其他文件引用了这个文件,那么生成go代码之后,会自动按照分号前的目录创建出目录,并且添加上引用
 option go_package = "github.com/test/pb;pb";
 ​
 message helloworld {
   required int32 id = 1; //不能用int,必须指定到具体的位数,仅支持32位以上,详细支持的类型可查看proto官网文档
   optional bool  boo = 2; //required是必须的,没有会报错,optional是可选的,可以没有
   repeated string str = 3; //相当于 []string,是复数的意思
   map<string, string> ma = 4; //gpt说proto2不支持map,但是实际我用工具生成出来的proto,是支持的,生成代码也没有报错
 }
 service worldhello {
   rpc testWorldHello(google.protobuf.Empty) returns (helloworld); //参数和返回值必须有且只有一个,不想要可以用google.protobuf.Empty
 }

proto3

 //和proto2很像,以下仅解释不同的地方
 syntax = "proto3";
 package test;
 import "A.proto";
 import "google/protobuf/empty.proto";
 option go_package = "github.com/test/pb;pb";
 ​
 message helloworld {
   int32 id = 1; //全都是可选的,没有optional和required了
   bool boo = 2;
   repeated string str = 3;
   map<string, string> ma = 4;
 }
 service worldhello {
   rpc testWorldHello(google.protobuf.Empty) returns (helloworld);
 }

protoc 命令

注意:对于根据proto生成go代码的场景,proto2生成的go代码,所有字段全都是指针;proto3生成的go代码,结构体全是指针,结构体内的字段是正常的。

-I 或 --proto_path= 指定引用的proto文件路径的

--cpp_out=,--python_out= 指定生成文件的位置和生成的语言的。protoc默认支持13种语言,

go_out 对应 protoc-gen-go 插件;

go-grpc_out 对应 protoc-gen-go-grpc 插件;

*_out 对应 protoc-gen-* 插件;

部分语言,如Go,需要手动安装一下插件,

 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@版本
 go install google.golang.org/grpc/cmd/protoc-gen-go@版本

对于版本较老的protoc-gen-go,命令如下:

 protoc -I $protodir --go_out=plugins=grpc:./genproto/  yourfile.proto

对于版本较新的protoc-gen-go,命令如下:

 protoc -I $protodir --go_out=./genproto/ --go-grpc_out=./genproto/  yourfile.proto

新版本会生成两个文件,一个结构,一个方法;老版本只有一个文件,结构和方法在一起。

通过protoc命令,可以同时生成出Client和Server端代码。不同进程可以依据生成出的代码完成交互。

附录

gpt4说的proto2和proto3的区别

Protocol Buffers(ProtoBuf)是 Google 开发的一种数据序列化协议,广泛用于网络通信和数据存储。ProtoBuf 有两个主要的版本:proto2proto3。以下是两者间的一些主要区别:

  1. 默认值:在 proto2 中,字段可以通过 optional 关键字指定为可选的,并可以为字段指定默认值。在 proto3 中,所有字段都是可选的,并且不能显式地为字段指定默认值。非指定的字段将使用其类型的零值。

  2. 字段存在性检查proto2 支持 HasField 方法来检查一个字段是否被设置。proto3 不支持这个特性,因为所有字段都是可选的,并且总是有值(如果没有显示设置,就是类型的零值)。

  3. 字段标记proto2 支持 requiredoptionalrepeated 标记。proto3 只支持 repeated 标记,所有未标记的字段都是可选的。

  4. 枚举类型proto3 的枚举类型必须从 0 开始定义,且第一个值必须为零值。proto2 对此没有要求。

  5. 兼容性proto3 是为了更简单和更好的跨语言兼容性而设计的。虽然它移除了一些 proto2 的特性,但是也增加了一些新的特性,如 Any 类型和 map 字段。

  6. 语言支持proto3 提供了更广泛的语言支持,包括 Java、C++、Python、Go、C#、Ruby、JavaScript 等。

这两个版本在使用上有一些不同,选择哪个版本需要根据你的具体需求和偏好来决定。

gpt4总结的protoc-gen-go和protoc-gen-go-grpc的区别

这个 StackOverflow 的问题主要关注的是 protoc-gen-goprotoc-gen-go-grpc 两个 protoc 插件之间的区别。

以下是这两个插件的主要区别:

  1. protoc-gen-go:这个插件用于生成 Protocol Buffers 的 Go 代码。它根据 .proto 文件生成消息类型和服务接口的定义。

  2. protoc-gen-go-grpc:这个插件用于生成 gRPC 的 Go 代码。它根据 .proto 文件中的服务定义生成实现 gRPC 服务和客户端所需的代码。

在早期,protoc-gen-go 插件同时生成 Protocol Buffers 和 gRPC 的代码。然而,自 protoc-gen-go v1.20.0 开始,生成 gRPC 代码的功能被移除。生成 gRPC 代码现在需要使用 protoc-gen-go-grpc 插件。

因此,如果你的 .proto 文件中只有消息类型的定义,你只需要 protoc-gen-go 插件。如果你的 .proto 文件中还包含服务定义,并且你需要生成 gRPC 服务和客户端的代码,你需要同时使用 protoc-gen-goprotoc-gen-go-grpc 插件。

例如,你可以通过以下命令来生成代码:

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

在这个命令中,--go_out 参数用于生成 Protocol Buffers 的代码,--go-grpc_out 参数用于生成 gRPC 的代码。. 表示生成的代码的输出目录,yourfile.proto 是你的 .proto 文件。

参考文档

终于有人把tcp、http、rpc和grpc总结完整了 - 知乎 (zhihu.com)

Protobuf通信协议详解:代码演示、详细原理介绍等 - 知乎 (zhihu.com)

proto参数区分之package和option go_package-CSDN博客

Go - 关于 protoc 工具的小疑惑 - 知乎 (zhihu.com)

Differences between protoc-gen-go and protoc-gen-go-grpc - Stack Overflow