目录
概述
gRPC是由Google开源的一个高性能rpc框架,由内部的Stubby演化而来,2015年正式开源,是云原生时代的rpc标准
核心设计思路
- 网络通信:自己封装了网络通信的部分,提供多语言的网络通信的封装(c/java(netty),go)
- 协议:http2,使用二进制传输、支持双向流(全双工)连接的多路复用
- 序列化:(基于文本如json,基于二进制,如java原生序列化),protobuf,时间和空间效率是json的3-5倍,使用IDL语言定义
- 代理的创建:让调用者像调用本地方法一样调用远程服务方法,stub
其他rpc框架
- ThriftRPC,使用专属协议基于tcp,性能高于gRPC,但gRPC在云原生时代与其他组件合作顺利,所以gRPC应用更广泛
gRPC好处:
- 高效的进程间通信(协议、序列化)
- 支持多语言,原生支持c/go/java,其他c语言之上的语言也支持c++/c#/nodejs/python/ruby/php...,是c的实现上做适配
- 支持多平台,linux/macos/windows/android/ios
- gRPC序列化方式采用protobuf,效率高
- 使用http2协议
- 大厂背书(google)
Http2.0协议
1.x协议
- 1.0:请求响应模式
- 短连接(无状态),由于早期硬件性能不足,http在请求响应后就断开连接。在技术层面使用HttpSession解决服务端与客户端(cookie)连接问题
- 文本传输、单工(无法实现服务器推送,只能采用客户端轮询方式实现)
- 1.1:请求响应模式,实现了有限的长连接(keepalived头),可以升级为websocket协议实现双工
- 1.x协议共性
- 文本格式传输数据,可读性好但是效率差
- 本质上无法实现双工
- 资源请求,需要发送多次请求,建立多个连接才能完成(css/js),优化方法(动静资源分离,cdn)
2.x协议
- 二进制协议,效率高于http1.x,可读性差
- 可以实现双工通信
- 一个请求,一个连接可以请求多个数据(多路复用)
http2.0的三个概念
- 数据流,stream,一个连接上可以发送多个流,每个流里面有一个消息,消息里面有帧,包括head数据、body数据
- 消息,message
- 帧,frame
其他概念
- 数据流的优先级,可以为不同的stream设置权重,限制不同流的传输顺序
- 流控,如client发送数据太快,server处理不过来,可以通知clien暂停发送
Protocol Buffers (protobuf)
- protobuf是一种与编程语言无关(IDL),与具体的平台无关(OS),定义中间语言,可以方便的在client与server之间传输
- 有两种版本,proto2,proto3,主流应用使用proto3
- 需要安装protobuf编译器,将proto的IDL语言转换成某一种开发语言
github地址:https://github.com/protocolbuffers/protobuf protobuf:https://protobuf.dev/
protobuf语法
idea中安装下protobuf插件(idea官方的即可,2021版本自带)
文件格式:.proto
版本声明:
syntax = "proto3"; //有2和3 使用3即可
注释:单行//
,多行/* */
与java相关的语法:
option java_multiple_files = false; //生成一个文件还是多个文件
option_java_package = "org.example"; //生成的类放在哪个包下
option java_outer_classname = "HelloService"; //生成的外部类的名字,管理内部类,开发实际使用的是内部类,针对的是message
逻辑包:
package xxx; //protobuf对于文件内容的管理
导入:
import "xxx/Xxx.proto"; //在一个proto文件中导入另外一个定义的文件
基本类型:参考官网,https://protobuf.dev/programming-guides/proto3/#scalar
枚举:
enum Season {
SPRING = 0; //必须从0开始
SUMMER = 1;
}
消息message:
message LoginRequest {
string username = 1; //1表示字段序号,范围1-2^29-1,19000-19999是proto保留,不能使用,不一定连续,唯一即可
stirng password = 2;
}
//singlular:这个字段的值只能有0个或1个(默认关键字)
//repeated:这个字段返回的数据是多个,等价于java中的list,实际使用时是一个list
//一个proto文件中可以定义多个消息
//消息是可以嵌套的
//oneof关键字,其中一个,表示该字段只能是定义的其中一个
message MyMessage {
string f1 = 1; //第一个字段
oneof abc { //第二个字段 是其中一个
string name = 2;
int32 age = 3;
}
}
服务定义:
service HelloService {
rpc hello(HelloRequest) resturns(HelloResponse) {}
//可以定义多个方法
}
//可以定义多个service
//有4个服务方式
gRPC开发
项目结构
xxx-api模块 //定义protobuf IDL语言(message、service),并且通过命令创建具体的代码,其他client、service引入使用
xxx-server模块 //实现api中定义的接口,发布rpc服务(创建服务端程序)
xxx-client模块 //创建服务stub(代理),基于代理进行rpc调用
一些开发经验:
- proto文件一般放在main下的proto目录下
- 一个proto对应一个文件,关闭多文件生成
- 外部类名称一般与proto对应,如HelloProto对应Hello.proto,实际使用的是HelloProto的内部类
maven插件:https://github.com/grpc/grpc-java ,直接在工程中编译proto
服务端:
- 服务端实现定义的服务Grpc基类,实现实际的service实现
- 调用onNext方法回传响应
- 调用onCompleted方法标记响应结束(服务端)
- 构建server,并注册服务
- 启动server
客户端:
- 通过代理对象完成rpc调用
- java中grpc是用netty实现的,需要获取channel
- 通过channel获取stub(代理)
- 准备调用参数
- 通过stub完成rpc调用
- 关闭channel
gRPC的四种通信方式
- 简单rpc,一元rpc(Unary RPC)
- 服务端流式rpc(Server Streaming RPC)
- 客户端流式rpc(Client Streaming RPC)
- 双向流rpc(Bi-directional Stream RPC)
一元RPC
当client发起调用后,提交数据,等待服务端响应。也即是传统的服务端客户端通信方式
服务端流式RPC
客户端发送一个请求对象,服务端返回多个结果对象。服务端可以在不同的时刻,返回结果
- 客户端的响应结果为一个迭代器用于获取服务端的多个结果(同步情况)
- 客户端异步情况下,参数中多了一个处理响应的异步观察者,用于处理服务端返回的结果(客户端可以控制参数,所以处理结果的异步逻辑在参数中)
在定义service时,需要在返回值前加stream
应用场景:如请求股票信息,只请求一次,后续返回多个结果
客户端流式RPC
客户端发送多个请求对象,服务端只返回一个结果
- 服务端在返回值中返回一个异步处理客户端请求的逻辑(客户端request的观察者,服务端可以控制返回值,所以处理客户端请求的逻辑在返回值中,跟同步相比,request的处理放在了返回值中)
- 客户端在rpc调用时返回值是一个request的观察者,与服务端的返回值对应,服务端返回客户端流的处理逻辑,客户端拿着服务端返回的客户端流进行发送数据,服务端和客户端用同一个Observer
在定义service时,需要在请求参数前加stream
应用场景:iot设备给服务端发送数据
双向流RPC
客户端可以发送多个消息给服务端,服务端也可以发送多个消息给客户端。
- 与客户端流rpc结构相同(是因为不管是服务端流rpc还是客户端流rpc,服务端处理响应都是StreamObserver)
应用场景:聊天室
gRPC的代理方式
- BlockingStub,阻塞
- Stub,异步,通过监听处理
- FutureStub,可以同步,也可以异步,类似NettyFuture,只能用于一元RPC
- 同步就使用get获取结果
- 异步可以通过addListener,这个无法获取返回值
- 异步可以使用工具类Futures.addCallback,可以获取返回值
gRPC与SpringBoot整合
整合思想:
- grpc-server
- grpc-client
- api层不管是不是用框架都要使用proto文件生成
SpringBoot与grpc整合过程中,对于服务端做了什么封装
- 原生的grpc
- 实现服务接口
- 创建服务端,发布grpc功能
- 如何封装
- 实现服务接口与具体业务相关,无法封装
- 服务端创建功能可以封装,但需要额外配置(端口 -- 配置,具体的服务实现 -- 注解)
注意:
- 使用的
grpc-spring-boot-starter
版本内部的grpc版本与外部的要一致,否则依赖冲突 - protoc-gen-grpc-java的版本要与使用的grpc的版本一致,protoc的版本要使用protobuf的版本,且要与grpc内部引用的protbobuf版本一致,否则编译的类报错
SpringBoot与grpc整合过程总,对客户单做了什么封装
- 原生grpc
- 通过channel设置服务端ip和port
- 获取stub,即远程服务代理,进行调用
- 如何封装
- 连接服务端,通过配置封装
- stub代理,通过spring容器封装
gRPC高级应用
- 拦截器 (一元拦截器)
- Stream Tracer (监听流,流拦截器)
- Retry Policy (客户端重试)
- NameResolver (配置中心)
- 负载均衡 (pick-first,轮询)
- grpc与微服务整合
- 序列化(protobuf)与dubbo整合
- grpc与dubbo整合,使用grpc通信
- grpc与GateWay整合
- grpc与JWT整合,做认证
- grpc与Nacos2.0整合,自定义配置中心
- grpc可以替换OpenFeign
- k8s可以与grpc无缝对接(k8s就是使用的grpc)