gRPC的官方介绍是:gRPC是一个现代的、高性能、开源的和语言无关的通用 RPC 框架,基于 HTTP2 协议设计,序列化使用PB(Protocol Buffer),PB 是一种语言无关的高性能序列化框架,基于 HTTP2+PB 保证了的高性能。
好吧我知道对于一个初学者来说这并没有意义,首先我们需要提炼关键,来回答以下几个问题,从而真正明白gRPC到底是什么?
RPC(Remote Procedure Call)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
gRPC是Google开发的一个RPC系统,它使用HTTP/2协议进行通信,并使用Protocol Buffers作为接口描述语言。
这使得gRPC比传统的RPC更加强大,可以支持流式请求和响应,以及其他复杂的交互模式。
总结:RPC 是一个框架,已经写好一些功能,但是并不适合谷歌的应用场景,在这个基础上 谷歌 对RPC 框架进行了迭代开发,形成了gRPC。
HTTP/2是HTTP协议的第二个主要版本,它在性能优化、服务器推送、头信息压缩、协议复用等方面都有显著的改进。
基于HTTP/2的设计意味着gRPC在设计时充分利用了HTTP/2的这些特性,
例如,它可以在一个TCP连接上并行发送多个请求和响应,减少了网络延迟。
总结:http2 是一个通讯规定,基于http2 的协议设计是指在使用这个通讯规定上进行的程序软件设计。
在明确上述两种协议的具体内容之后,我们就可以回答这个问题了。在gRPC中RPC是它的业务逻辑基本框架,HTTP2 是它的通讯逻辑基本原理。
从而我们可以理解这张图:
![[Pasted image 20231114145840.png]]
Protocol Buffers(简称PB)是Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。
它能够将数据结构以高效和可扩展的格式进行序列化,可以用于网络通信和数据存储等场景。
上面的解释很绕,让我们先明确什么是序列化:简单点说:就是把信息按照固定格式组织起来,为的是解决不同系统、不同编程语言、不同平台之间的传输存储问题。
序列化是指将对象转换为可以在网络传输或存储中使用的格式,以便在需要时可以重新构造出原始对象。在序列化过程中,对象的状态被转换为字节流或其他可传输的形式,以便可以在不同的系统、编程语言或平台之间进行传输或存储。
序列化的主要目的是实现对象的持久化、跨网络传输和跨平台通信。通过序列化,可以将对象转换为字节流,然后可以将字节流保存到文件中、通过网络发送给其他系统,或者在不同的程序之间进行交互。在接收方,可以通过反序列化将字节流转换回原始对象,以便进行进一步的处理或使用。
常见的序列化格式包括JSON、XML和Protocol Buffers(Protobuf)等。不同的序列化格式具有不同的特点和适用场景,选择合适的序列化格式取决于具体的需求和使用环境。
怎么用呢?这里我们以PB为例:
Protocol Buffers(简称Protobuf)使用特定的语法和编译器来定义消息的结构和字段,然后通过编译器生成相应的代码,用于序列化和反序列化对象。
下面是使用C++代码的示例,展示了如何使用Protobuf进行对象的序列化和反序列化:
定义消息结构
首先,需要使用Protobuf的语法定义消息的结构和字段。例如,创建一个名为Person
的消息类型,包含name
和age
字段:
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
}
生成代码
使用Protobuf的编译器将上述定义的消息结构编译为相应的代码。在C++中,可以使用protoc
命令行工具进行编译:
protoc --cpp_out=. person.proto
这将生成person.pb.h
和person.pb.cc
两个文件,包含了用于序列化和反序列化Person
消息的代码。
序列化对象
在C++代码中,可以使用生成的代码来创建Person
对象,并将其序列化为字节流:
#include "person.pb.h"
// 创建Person对象
Person person;
person.set_name("John");
person.set_age(25);
// 序列化为字节流
std::string serialized_data;
person.SerializeToString(&serialized_data);
SerializeToString
方法将Person
对象序列化为一个字符串。
反序列化对象
反之,可以将序列化的字节流反序列化为原始的Person
对象:
// 反序列化为Person对象
Person deserialized_person;
deserialized_person.ParseFromString(serialized_data);
// 访问反序列化后的字段
std::string name = deserialized_person.name();
int age = deserialized_person.age();
ParseFromString
方法将字节流解析为Person
对象,并可以通过访问相应的字段获取数据。
需要注意的是,上述示例中的代码是使用C++语言进行的,如果使用其他编程语言,操作步骤会有所不同,但基本原理是相似的。
在通过上述 的详细介绍之后,我们就可以介绍gRPC 的工作方式来说明什么是gRPC 了。
![[Pasted image 20231114151128.png]]
上图中使用的是c++和红宝石以及安卓开发三种语言、平台、系统都不同的实例间通讯,展示的是gRPC 的 通用性
![[Pasted image 20231114151324.png]]
上图是解释了信息在gRPC 中是怎样流转的。展示的是gRPC 的 序列化
gRPC基于服务的思想:定义一个服务,描述这个服务的方法以及入参出参,服务器端有这个服务的具体实现,客户端保有一个存根,提供与服务端相同的服务。
gRPC默认采用protocol buffer作为IDL(Interface Description Lanage)接口描述语言,服务之间通信的数据序列化和反序列化也是基于protocol buffer的,因为protocol buffer的特殊性,所以gRPC框架是跨语言的通信框架(与编程语言无关性),也就是说用Java开发的基于gRPC的服务,可以用GoLang编程语言调用
gRPC同时支持同步调用和异步调用,同步RPC调用时会一直阻塞直到服务端处理完成返回结果,异步RPC是客户端调用服务端时不等待服务段处理完成返回,而是服务端处理完成后主动回调客户端告诉客户端处理完成
gRPC是基于http2协议实现的,http2协议提供了很多新的特性,并且在性能上也比http1提搞了许多,所以gRPC的性能是非常好的
gRPC并没有直接实现负载均衡和服务发现的功能,但是已经提供了自己的设计思路。已经为命名解析和负载均衡提供了接口
基于http2协议的特性:gRPC允许定义如下四类服务方法
低延迟,高度可扩展的分布式系统
开发与云服务器通信的客户端
设计一个准确,高效,且与语言无关的新协议时
分层设计,以实现扩展,例如。身份验证,负载平衡,日志记录和监控等
谷歌长期以来一直在gRPC中使用很多基础技术和概念。目前正在谷歌的几个云产品和谷歌面向外部的API中使用。Square,Netflix,CoreOS,Docker,CockroachDB,Cisco,Juniper Networks以及许多其他组织和个人也在使用它。
这里我们主要介绍pb的工作方式,即怎样封装数据;
gRPC 使用的是自己的封装方式 Protocol Buffer
简称ProtoBuf,是Google开发的一种跨语言、跨平台、可扩展的用于序列化数据协议。
下面是一个protoBuf 的样例
// XXXX.proto
// rpc服务的类 service关键字, Test服务类名
service Test {
// rpc 关键字,rpc的接口
rpc HowRpcDefine (Request) returns (Response) ; // 定义一个RPC方法
}
// message 类,c++ class
message Request {
//类型 | 字段名字| 标号
int64 user_id = 1;
string name = 2;
}
message Response {
repeated int64 ids = 1; // repeated 表示数组
Value info = 2; // 可嵌套对象
map<int, Value> values = 3; // 可输出map映射
}
message Value {
bool is_man = 1;
int age = 2;
}
以上是一个使用样例,包含方法定义、入参、出参。可以看出有几个明确的特点:
但.proto文件并不是代码,不能执行,要想直接跨语言是不行的,必须得有对应语言的中间代码才行,中间代码要有以下能力:
在完成数据封装之后,我们就准备好了将它传输出去,接下来就是数据传输了。
grpc采用HTTP2.0,相对于HTTP1.0 在 更快的传输 和 更低的成本 两个目标上做了改进。有以下几个基本点:
- HTTP2 未改变HTTP的语义(如GET/POST等),只是在传输上做了优化
- 引入帧、流的概念,在TCP连接中,可以区分出多个request/response
- 一个域名只会有一个TCP连接,借助帧、流可以实现多路复用,降低资源消耗
- 引入二进制编码,降低header带来的空间占用
HTTP1.1核心问题在于:在同一个TCP连接中,没办法区分response是属于哪个请求,一旦多个请求返回的文本内容混在一起,则没法区分数据归属于哪个请求,所以请求只能一个个串行排队发送。这直接
导致了TCP资源的闲置。
HTTP2为了解决这个问题,提出了 流 的概念,每一次请求对应一个流,有一个唯一ID,用来区分不同的请求。基于流的概念,进一步提出了 帧 ,一个请求的数据会被分成多个帧,方便进行数据分割传输,每个帧都唯一属于某一个流ID,将帧按照流ID进行分组,即可分离出不同的请求。这样同一个TCP连接中就可以同时并发多个请求,不同请求的帧数据可穿插在一起,根据流ID分组即可。HTTP2.0基于这种二进制协议的乱序模式 (Duplexing),直接解决了HTTP1.1的核心痛点,通过这种复用TCP连接的方式,不用再同时建多个连接,提升了TCP的利用效率。
总结
传输使用 http2 协议,是在tcp链接完成之后,再在应用层发送时的一个协议,通过流 ID 和 帧报文的两个概念 解决了传输拥塞的问题。我们将这种方式称之为 tcp链接复用。
gRPC的官方介绍是:gRPC是一个现代的、高性能、开源的和语言无关的通用 RPC 框架,基于 HTTP2 协议设计,序列化使用PB(Protocol Buffer),PB 是一种语言无关的高性能序列化框架,基于 HTTP2+PB 保证了的高性能。
好吧我知道对于一个初学者来说这并没有意义,首先我们需要提炼关键,来回答以下几个问题,从而真正明白gRPC到底是什么?
RPC(Remote Procedure Call)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
gRPC是Google开发的一个RPC系统,它使用HTTP/2协议进行通信,并使用Protocol Buffers作为接口描述语言。
这使得gRPC比传统的RPC更加强大,可以支持流式请求和响应,以及其他复杂的交互模式。
总结:RPC 是一个框架,已经写好一些功能,但是并不适合谷歌的应用场景,在这个基础上 谷歌 对RPC 框架进行了迭代开发,形成了gRPC。
HTTP/2是HTTP协议的第二个主要版本,它在性能优化、服务器推送、头信息压缩、协议复用等方面都有显著的改进。
基于HTTP/2的设计意味着gRPC在设计时充分利用了HTTP/2的这些特性,
例如,它可以在一个TCP连接上并行发送多个请求和响应,减少了网络延迟。
总结:http2 是一个通讯规定,基于http2 的协议设计是指在使用这个通讯规定上进行的程序软件设计。
在明确上述两种协议的具体内容之后,我们就可以回答这个问题了。在gRPC中RPC是它的业务逻辑基本框架,HTTP2 是它的通讯逻辑基本原理。
从而我们可以理解这张图:
![[Pasted image 20231114145840.png]]
Protocol Buffers(简称PB)是Google开发的一种数据序列化协议(类似于XML、JSON、hessian)。
它能够将数据结构以高效和可扩展的格式进行序列化,可以用于网络通信和数据存储等场景。
上面的解释很绕,让我们先明确什么是序列化:简单点说:就是把信息按照固定格式组织起来,为的是解决不同系统、不同编程语言、不同平台之间的传输存储问题。
序列化是指将对象转换为可以在网络传输或存储中使用的格式,以便在需要时可以重新构造出原始对象。在序列化过程中,对象的状态被转换为字节流或其他可传输的形式,以便可以在不同的系统、编程语言或平台之间进行传输或存储。
序列化的主要目的是实现对象的持久化、跨网络传输和跨平台通信。通过序列化,可以将对象转换为字节流,然后可以将字节流保存到文件中、通过网络发送给其他系统,或者在不同的程序之间进行交互。在接收方,可以通过反序列化将字节流转换回原始对象,以便进行进一步的处理或使用。
常见的序列化格式包括JSON、XML和Protocol Buffers(Protobuf)等。不同的序列化格式具有不同的特点和适用场景,选择合适的序列化格式取决于具体的需求和使用环境。
怎么用呢?这里我们以PB为例:
Protocol Buffers(简称Protobuf)使用特定的语法和编译器来定义消息的结构和字段,然后通过编译器生成相应的代码,用于序列化和反序列化对象。
下面是使用C++代码的示例,展示了如何使用Protobuf进行对象的序列化和反序列化:
定义消息结构
首先,需要使用Protobuf的语法定义消息的结构和字段。例如,创建一个名为Person
的消息类型,包含name
和age
字段:
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
}
生成代码
使用Protobuf的编译器将上述定义的消息结构编译为相应的代码。在C++中,可以使用protoc
命令行工具进行编译:
protoc --cpp_out=. person.proto
这将生成person.pb.h
和person.pb.cc
两个文件,包含了用于序列化和反序列化Person
消息的代码。
序列化对象
在C++代码中,可以使用生成的代码来创建Person
对象,并将其序列化为字节流:
#include "person.pb.h"
// 创建Person对象
Person person;
person.set_name("John");
person.set_age(25);
// 序列化为字节流
std::string serialized_data;
person.SerializeToString(&serialized_data);
SerializeToString
方法将Person
对象序列化为一个字符串。
反序列化对象
反之,可以将序列化的字节流反序列化为原始的Person
对象:
// 反序列化为Person对象
Person deserialized_person;
deserialized_person.ParseFromString(serialized_data);
// 访问反序列化后的字段
std::string name = deserialized_person.name();
int age = deserialized_person.age();
ParseFromString
方法将字节流解析为Person
对象,并可以通过访问相应的字段获取数据。
需要注意的是,上述示例中的代码是使用C++语言进行的,如果使用其他编程语言,操作步骤会有所不同,但基本原理是相似的。
在通过上述 的详细介绍之后,我们就可以介绍gRPC 的工作方式来说明什么是gRPC 了。
)
包浆老图
上图中使用的是c++和红宝石以及安卓开发三种语言、平台、系统都不同的实例间通讯,展示的是gRPC 的 通用性
上图是解释了信息在gRPC 中是怎样流转的。展示的是gRPC 的 序列化
gRPC基于服务的思想:定义一个服务,描述这个服务的方法以及入参出参,服务器端有这个服务的具体实现,客户端保有一个存根,提供与服务端相同的服务。
gRPC默认采用protocol buffer作为IDL(Interface Description Lanage)接口描述语言,服务之间通信的数据序列化和反序列化也是基于protocol buffer的,因为protocol buffer的特殊性,所以gRPC框架是跨语言的通信框架(与编程语言无关性),也就是说用Java开发的基于gRPC的服务,可以用GoLang编程语言调用
gRPC同时支持同步调用和异步调用,同步RPC调用时会一直阻塞直到服务端处理完成返回结果,异步RPC是客户端调用服务端时不等待服务段处理完成返回,而是服务端处理完成后主动回调客户端告诉客户端处理完成
gRPC是基于http2协议实现的,http2协议提供了很多新的特性,并且在性能上也比http1提搞了许多,所以gRPC的性能是非常好的
gRPC并没有直接实现负载均衡和服务发现的功能,但是已经提供了自己的设计思路。已经为命名解析和负载均衡提供了接口
基于http2协议的特性:gRPC允许定义如下四类服务方法
低延迟,高度可扩展的分布式系统
开发与云服务器通信的客户端
设计一个准确,高效,且与语言无关的新协议时
分层设计,以实现扩展,例如。身份验证,负载平衡,日志记录和监控等
谷歌长期以来一直在gRPC中使用很多基础技术和概念。目前正在谷歌的几个云产品和谷歌面向外部的API中使用。Square,Netflix,CoreOS,Docker,CockroachDB,Cisco,Juniper Networks以及许多其他组织和个人也在使用它。
这里我们主要介绍pb的工作方式,即怎样封装数据;
gRPC 使用的是自己的封装方式 Protocol Buffer
简称ProtoBuf,是Google开发的一种跨语言、跨平台、可扩展的用于序列化数据协议。
下面是一个protoBuf 的样例
// XXXX.proto
// rpc服务的类 service关键字, Test服务类名
service Test {
// rpc 关键字,rpc的接口
rpc HowRpcDefine (Request) returns (Response) ; // 定义一个RPC方法
}
// message 类,c++ class
message Request {
//类型 | 字段名字| 标号
int64 user_id = 1;
string name = 2;
}
message Response {
repeated int64 ids = 1; // repeated 表示数组
Value info = 2; // 可嵌套对象
map<int, Value> values = 3; // 可输出map映射
}
message Value {
bool is_man = 1;
int age = 2;
}
以上是一个使用样例,包含方法定义、入参、出参。可以看出有几个明确的特点:
但.proto文件并不是代码,不能执行,要想直接跨语言是不行的,必须得有对应语言的中间代码才行,中间代码要有以下能力:
在完成数据封装之后,我们就准备好了将它传输出去,接下来就是数据传输了。
grpc采用HTTP2.0,相对于HTTP1.0 在 更快的传输 和 更低的成本 两个目标上做了改进。有以下几个基本点:
- HTTP2 未改变HTTP的语义(如GET/POST等),只是在传输上做了优化
- 引入帧、流的概念,在TCP连接中,可以区分出多个request/response
- 一个域名只会有一个TCP连接,借助帧、流可以实现多路复用,降低资源消耗
- 引入二进制编码,降低header带来的空间占用
HTTP1.1核心问题在于:在同一个TCP连接中,没办法区分response是属于哪个请求,一旦多个请求返回的文本内容混在一起,则没法区分数据归属于哪个请求,所以请求只能一个个串行排队发送。这直接
导致了TCP资源的闲置。
HTTP2为了解决这个问题,提出了 流 的概念,每一次请求对应一个流,有一个唯一ID,用来区分不同的请求。基于流的概念,进一步提出了 帧 ,一个请求的数据会被分成多个帧,方便进行数据分割传输,每个帧都唯一属于某一个流ID,将帧按照流ID进行分组,即可分离出不同的请求。这样同一个TCP连接中就可以同时并发多个请求,不同请求的帧数据可穿插在一起,根据流ID分组即可。HTTP2.0基于这种二进制协议的乱序模式 (Duplexing),直接解决了HTTP1.1的核心痛点,通过这种复用TCP连接的方式,不用再同时建多个连接,提升了TCP的利用效率。
总结
传输使用 http2 协议,是在tcp链接完成之后,再在应用层发送时的一个协议,通过流 ID 和 帧报文的两个概念 解决了传输拥塞的问题。我们将这种方式称之为 tcp链接复用。