将单体应用拆成多个微服务,之间如何通信,如果还是用HTTP或者Json,就会存在一些问题。
例如使用json,
- 文本冗余:json是文本,字段名要在每条消息里重复一遍,网络上跑的全是冗余。
- 靠文档约定:
- 手写封装:每接一个新服务,又要手写一遍请求封装和响应解析
gRPC使用protocol buffers定契约,用HTTP/2传数据,主打高性能和强类型。
RPC:远程过程调用,之前函数在本地调用,如果函数在另外一个机器上,就需要拼URL, 发HTTP请求,解析JSON响应,RPC就是把这些网络细节全部藏起来,感觉还是本地调用。
其实中间多了一层代理对象Stub桩,调Stub,负责把参数打包、发出去、等回复,再拆包还给你。服务端有一份对应的骨架代码,收到请求后调用真正的实现。
gRPC就是这样一套框架,契约用Protobuf, 传输走HTTP/2,
用gRPC第一步不是写代码,而是定义.proto文件,是接口描述语言,专门用来定义数据长什么样,有哪些方法。
= 后面是编号,编号才是二进制里的真正标识,字段名只存在于源码里。
改字段名不影响兼容,但编号一旦用过,就绝对不能改,不能复用。
service定义服务,里面每个rpc就是一个能被远程调用的方法。入参和返回类型都写死在契约里。
proto里只写编码和值,同一条数据通常比json小三到十倍,解析也快的多。
.proto写好之后,用protoc这个编译器来生成代码,本身负责解析,具体生成哪种语言,交给插件决定。
生成的东西包括:
- Client Stub:客户端调用,客户端直接调用,网络的事就全负责了
- Server接口:你来实现业务逻辑
这样做的好处是两边类型完全对齐,字段拼错在编译器就会报错。
团队协作之间就是传的.proto文件,接口一改,双方重新生成一遍,谁没跟上,编译直接过不去。
gRPC快,很大一部分归功于选择了HTTP/2,
HTTP/1一条连接同一时间只能处理一个请求,前一个响应没回来,后面的就得排队,这叫队头阻塞。浏览器智能多开几条连接环节,服务端的压力就上去了。
HTTp/2把数据切成二进制帧,每个帧都带着所属流的编号,于是多个请求可以同时跑在一条TCP连接上,交错传输,互不阻塞。这就是多路复用,也是gRPC能抗高并发的基础。头部还会用HPACK压缩,重复的头信息不用一遍遍地发。长连接复用省掉了大量的握手开销。
gRPC的四种调用方式:
- 一元调用
- 服务端流:一次请求,服务端返回多条,适合分块下载大文件,拉取历史消息,订阅一段时间的数据
- 客户端流:客户端连续发很多条,服务端最后返回一个结果。适合分片上传文件,批量上报埋点,最后回一个汇总
- 双向流:两边随时发,适合聊天、实时推送、语音识别这类场景
上述方式都是跑在HTTP/2连接上,这是REST很难做到的,
除了业务参数,一次调用还可携带附加信息。MetaData,
注意Deadline,明确时间点,整条链路共用,到点没完成,整条链路一起放弃,不会有服务在那儿傻等。
内部服务之间用gRPC,对外和对浏览器用REST,这是最常见的组合。