一、简介
我们在前面学习了thrift rpc的知识,我们从其中接触到了IDL,编解码协议,服务的远程调用(调用远程服务就像在在本地调用一样)等各种概念。
其实我个人对thrift的使用并不多,我更多的是使用今天我们要提到的一个RPC框架称之为gRPC,其实作为一个rpc框架,grpc和之前提到的thrift是有很多相似之处的,包括他也有IDL这个概念。只是语法不一样而已。
但是作为一个非常流行的rpc框架,grpc是有自己的独到设计的,下面我们来看看他的一些特点和概念。
1. gRPC 是由google开源的一个高性能的RPC框架。是由Google内部的Stubby RPC,演化而来的,2015正式开源。在如今大行其道的云原生时代,实际上它已经是一个事实上的RPC标准。
2. gRPC 核心的设计思路
1. 网络通信: gRPC自己封装网络通信的部分 提供多种语言的 网络通信的封装 (C Java[Netty] GO),所以他也是有多语言异构的能力的。他也是通过IDL来实现语言无关的翻译能力。
2. 协议:使用HTTP2作为网络传输协议,传输数据的时候数据体使用二进制数据内容。 支持双向流(双工)连接的多路复用。
3. 序列化:较之于传统的http1的基本文本JSON格式,http2是基于二进制内容序列化的。具体的话,他是使用了一种叫做protobuf (Protocol Buffers) 的序列化方式,这是google开源一种序列化方式,时间效率和空间效率是JSON的3---5倍。IDL语言
4. 代理的创建 :让调用者像调用本地方法那样 去调用远端的服务方法。
我们可以看到,上面四点,其实只需要我们处理3,使用protobuf来定义IDL,实现序列化,数据消息翻译成目标语言。其余的grpc都封装好了给我们。
3. gRPC 与 ThriftRPC 区别
共性:支持异构语言的RPC。
区别:
1. 网络通信 Thrift 的第五层是自己定义的专属协议,而GRPC则是选择了HTTP2
2. 性能角度 ThriftRPC 性能 高于 gRPC,毕竟他是自己实现的协议,更加精准。
3. gRPC 大厂背书(Google),云原生时代 与其他组件合作的顺利。所以gRPC应用更广泛,nacos中就有使用。
4. gRPC的好处
1. 高效的进行进程间通信。
2. 支持多种语言 原生支持 C Go Java实现。C语言版本上扩展 C++ C# NodeJS Python Ruby PHP..
3. 支持多平台运行 Linux Android IOS MacOS Windows。
4. gPRC序列化方式采用protobuf,效率高。
5. 使用Http2协议
6. 大厂的背书(谷歌)
二、关于http2
实际上网上关于http2的内容已经很多了。我这里就不做太多的赘述了,我来简单描述一下,如果有什么不足之处,大家多多指教。
1、Http1.x协议
1.1、http1.0
1、Http1.0协议时代,他是基于请求响应的模式,属于一种短连接协议,也就是你发送请求接受相应之后就立刻断开。所以他是无状态的,无状态的意思就是服务端不知道你客户端是谁,因为你每次都断开。但是实际上我们开发的时候往往需要记住状态,所以大家设计了cookie和session的机制来记住,每次你带着你的标记来。但是这机制毕竟不是网络协议的内容,是技术人员的一种弥补技术手段。
2、他是传输数据文本结构,你可能会说有时候我上传文件,明明传的是二进制的,数据是二进制的,但是其余的请求头,标记等等都是文本。
3、单工模式, 无法实现服务端主动推送,为了变相实现推送一般使用客户端轮训的方式,也就是客户端不断的去询问,达到一种看似感知服务端的效果。但是大部分是空轮训,浪费资源了。
1.2、http1.1
Http1.1协议依然是请求响应的模式,但是已经进化成为了一种有限的长连接,通过keepalive请求头标识来保持一段时间的长连接,而且可以通过 升级的方式实现WebSocket 的双工协议,进而实现服务器向客户端推送。
Http1.x协议 共性
1. 传输数据文本格式,可读性好的但是效率差。
2. 本质上Http1.x协议无法实现双工通信。
3. 资源的请求。比如我们要像服务器请求三个资源,就需要发送三次请求,建立多个连接才可以完成。(有时候你发接口请求,需要请求一些静态资源,就要多次请求获取)
2. HTTP2.0协议
1. Http2.0协议是一个二进制协议,效率高于Http1.x协议,可读性差。
2. 可以实现双工通信。
3. 一个请求 一个连接 可以请求多个数据。【多路复用】
# Http2.0协议的三个概念
1. 数据流 stream
2. 消息 message
3. 帧 frame 参看图
# 其他的相关概念
1. 数据流的优先级,可以通过为不同的stream设置权重,来限制不同流的传输顺序。
2. 流控 client发送的数据太快了,server处理不过来,通知client暂停数据的发送。
我们来画一个图来表示一下http2的这些概念。
我们看到它把以前我们需要建立三次连接获取三次数据的格式变成了,建立一次连接,通过流的形式发送过去,然后响应回来。而且这个还可以调整stream之间的权重来选择先响应哪个后响应哪个。
三、Protocol Buffers [protobuf]
1. protobuf 是一种与编程语言无关【IDL】,与具体的平台无关【OS】。他定义的中间语言,可以方便的在client 于 server中进行RPC的数据传输。
2. protobuf 两种版本 proto2 proto3,但是目前主流应用的都是proto3。
3. protobuf主要安装protobuf的编译器,编译器目的,可以把protobuf的IDL语言,转换成具体某一种开发语言。
1、protobuf编译器的安装
下载地址:https://github.com/protocolbuffers/protobuf/releases
windows 版本
1. 直接解压缩 方式在一个特定的目录下面
2. 直接配置环境变量 path
mac版本
brew install protobuf
protoc --version 来检验是否安装
2、protobuf IDEA的插件
1. 2021.2版本后面的新版本 IDEA内置了Protobuf插件
2. 之前版本 可以选装第三方Protobuf插件
3. 二者不能共存。
3、protobuf的语法
其实官方提供了详细的语法介绍,我们可以去参考一下protobuf的官方文档
3.1、文件格式
所有的proto内容都要写在.proto后缀的文件中。比如
UserService.proto
OrderService.proto
3.2、版本设定
syntax = “proto3”;
文件最上面声明出版本号。
3.3、注释
1. 单行注释 //
2. 多行注释 /* */
3.4、与Java语言相关的语法
#后续protobuf生成的java代码 一个源文件还是多个源文件 xx.java,自己开发一般一个简单,有时候实际开发会按照业务拆分多个组织,看要求。
option java_multiple_files = false;
#指定protobuf生成的类 放置在哪个包中,java的包
option java_package = "com.levi";
#指定的protobuf生成的外部类的名字(管理内部类【内部类才是真正开发使用】)
option java_outer_classname = "UserServce";
在protobuf中我们的这些业务类都是有一个外部类的,他的作用就是管理这些业务类。这就是他的规范。
option的意思就是可以选,所以以上这些你都可以不写,有默认的。最好味了规范还是写上。
3.5、逻辑包
这个用的不多,他的作用是用来管理多个proto文件,不是管理包划分。而是一种proto的划分,类似于这个proto属于这个逻辑包下这种区分。用来结构管理。
# 对于protobuf对于文件内容的管理
package xxx;
3.6、导入
UserService.proto
OrderService.proto
import "xxx/UserService.proto";
OrderService.proto导入了UserService.proto,就可以在OrderService.proto中使用UserService.proto中定义的内容了。
3.7、基本类型
类型中定义了proto文件中的proto类型和各个语言的基本类型的对应,可以看一下,我们这里主要观察java即了。
3.8、枚举类型
enum SEASON{
SPRING = 0;
SUMMER = 1;
}
枚举的值 必须是0开始 ,后面那个是标号,不一定连续,但是一定要从0开始递增。
grpc的标号和thrift不一样,他是放在=后面的。
3.9、消息 Message
其实就是实体类,thrift是struct那些,grpc直接就是message。
message LoginRequest {
string username = 1;
singular string password = 2;
int32 age = 3;
}
编号 从1开始 到2^29-1 注意:19000 - 19999 不能用这个区间内的编号,因为他是protobuf自己保留的。这个编号不需要连续,递增就好了。
# 一组关键字
- singular : 这个字段的值 只能是0个或1个 (默认关键字) null "123456"
- repeated :返回集合,proto会生成对应的getList方法。
message Result{
string code = 200;
repeated string datas = 2; //这个字段 返回值 是多个 比如我们分页要获取多个回来集合
}
# 可以定义多个消息
message LoginRequest{
....
}
message LoginResponse{
...
}
# 消息可以嵌套
message SearchResponse{
message Result{
string url = 1;
string title = 2;
}
string xxx = 1;
int32 yyy = 2;
# 可以直接使用嵌套
Result ppp = 3;
}
message AAA{
string xxx = 1;
SearchResponse.Result yyy = 2; # 直接.引用内部即可
}
# oneof [其中一个],标识这个类型的东西虽然里面有多个属性,但是只会返回一个
message SimpleMessage{
oneof test_oneof{
string name = 1;
int32 age = 2;
}
# test_oneof这个属性只会返回name或者age一个。
test_oneof xxx
}
3.10、服务service
就是service业务类。
service HelloService{
rpc hello(HelloRequest) returns(HelloResponse){}
}
# 里面是可以定义多个服务方法。
# 定义多个服务接口
# gPRC 服务 4种服务方式,后续介绍 。