导语
微服务架构语境下,我们经常会聊到 RPC协议、RPC框架 等名词,但其实很多同学并没有真正理解这些术语的含义,只是一个模糊的概念。
本身试图用比较间接的语言解释RPC的相关概念,以及我们为什么使用RPC(框架)
什么是RPC RPC,即Remote Procedure Call ,语义是远程过程调用. 简单的说,RPC就是在一个应用程序/服务中像本地函数调用一样去访问网络上的另一个应用程序/服务中的函数。
如果Application-A可以调用Application-B的function-B1函数和Application-C的function-C1函数
从广义上讲,Redis-cli、MySQL客户端与其服务器进行数据存储通信,也可以将其视为RPC。
但是我们一般说的RPC是指像gRPC等约定了通用的调用语法、内容编码格式的框架,以及他们使用的网络传输协议。
讨论RPC主要关注一下三个方面:
调用语义: 即接口语法,如何进行远程调用,以及它是如何实现的内容编码: 数据是如何打包传输的,纯文本?二进制?网络传输: 使用什么网络协议通信, TCP?UDP?HTTP?调用语义RPC的调用语义,通常有以下几类:
桩代码: 直接根据IDL协议文件等生成桩代码,每个接口的调用都生成了本地接口函数。SDK或依赖库: 引入SDK或者依赖库,在SDK或者库中已经封装了对应服务接口的请求函数。但是封装的颗粒度可能不如桩代码那么细。内容编码RESTfull HTTP JSON: body直接打包成Json文本传输,同时需要Header来做一些内容编码约定。 使用灵活,但是会存在冗余数据,压缩性能差。同时无法清晰、准确地描述复杂的结构体。gRPC HTTP2.0 Protobuf: 二进制编码,对字段名做了优化,有超高的压缩率。其他: gRPC HTTP2.0 Protobuf已经是业界较为流行的优秀方案,其他的方案通常是在其基础上做了优化和拓展。网络传输网络传输部分,在设计RPC框架时是相对复杂的部分,需要考虑字节流如何分隔报文,是否需要使用异步变成模型,超时丢包如何处理等。
这一部分对RPC框架使用者而言可能不需要了解特别深入。
gRPC与Protocol Buffers gRPCgRPC是一个高性能的、开源通用的通用RPC框架,也是目前最流程的RPC框架。 关于gRPC的详细内容开源参考gRPC官网
本文主要以gRPC作为一个范例,讨论gRPC是如何实现RPC框架中的调用语义、内容编码、网络传输。以及对比HTTP等协议的优势。
Protocol Buffers 简介Protocol Buffers 是gRPC最重要的部分之一,它是gRPC调用语义和内容编码的实现基础。
IDL: 接口描述(定义)语言,interface description language。 用于以语言无关的方式描述一个服务/组件的API,
Protocol Buffers: 是Google开发的一种跨语言、跨平台、可扩展的用于序列化数据协议。参考Protocol Buffers
Protobuf中的proto文件就是IDL的一种具体实现。
proto文件示例:
代码语言:javascript代码运行次数:0运行复制1. syntax = "proto3";
2. package helloworld;
3. // The greeting service definition.
4. service Greeter {
5. // Sends a greeting
6. rpc SayHello (HelloRequest) returns (HelloReply) {}
7. }
8.
9. // The request message containing the user's name.
10. message HelloRequest {
11. string name = 1;
12. }
13.
14. // The response message containing the greetings
15. message HelloReply {
16. string message = 1;
}
开发者在proto文件中定义每一个接口的调用方式、请求包格式、应答包格式。 然后利用工具,将其转换为各种语言的桩代码。
服务侧实现桩代码中定义的接口。
客户端侧则通过桩代码中的函数,调用接口。
protocol buffer协议的特点指定字段类型: 每个字段的类型是确定的给每个字段分配序号: 每个字段都分配了一个标号。在数据压缩时及其重要。指定字段规则(单数、复数repeated): 方便表示数组允许嵌套: 能够表示复杂的数据结构定义了接口方法和参数: 明确了调用语义,依赖双方需要同时持有这个文件,并依此进行编解码protobuffer 与 HTTP-Json等对比1. protobuf 有更高的压缩率
以下面包体为例
代码语言:javascript代码运行次数:0运行复制{
"UserID": 101,
"UserName": "windeal"
}
如果使用HTTP-Json,则包体的内容会全部打包为string
"{\"UserID\":10001,\"UserName\":\"windeal\"}"
首先,字段名根据其名称的字符数占用对应的字节
其次,数字变为字符串,通常会占用更多的字节。转为string就需要占用3个字节;
如果使用Protobuf的编码Encoding, 字段类型可以使用标号替代,占用的体积更少。 数字也会使用Base 128 Varints编码使得占用的空间体积更小。
2. protobuf 更快的序列化/反序列化效率
因为protobuf的类型明确,解析策略简单(得益于其编码), 因而有更高的序列化和反序列化效率。
3. protobuf的可读性比HTTP-Json差 (缺点)
因为protobuf序列化后是一些二进制字节,而且字段名也被替换为标号了, 因为肉眼是无法识别其含义的。不具备可读性。
gRPC 与 HTTP2 Protocol Buffer 解决的是gRPC的调用语义和内容编码的部分。
gRPC用HTTP2协议来进行网络传输。
gRPC底层是支持多种网络通信协议来进行网络传输的,但是目前讨论的gRPC一般都是指基于HTTP2的gRPC。
gRPC为什么选择HTTP2可以考虑以下几个方面
HTTP2对比直接基于TCP、UDP通信,可能性能稍差,单对于RPC框架而已还是可以接受的。泛HTTP协议已经在网络服务中被广泛使用, 经过大量实践检验。HTTP2 解决了HTTP1.1协议的不足。HTTP2本身的内容较多,后面会考虑单独写一篇文章介绍。
本文主要从RPC 网络传输关注的几个点,简单介绍下HTTP2相比HTTP1.1的优势。
头部压缩
HTTP 1.1 会携带大量的头部,存在大量冗余文本,而且没有较好的压缩方案, 从而导致报文的体积较大。
HTTP2 使用header静态表为高频的header建立索引,这样header就可以向body那样讲header-key转为标号来进行压缩。从而大大减少了传输体积。
多路复用
HTTP/1.1是基于纯文本的,这导致其消息传递是“管道串形化”的:在同一个TCP连接中只有等一个消息完成之后,才能进行下一条消息;否则客户端无法识别收到的Response是属于哪一个Request。
HTTP2 是基于二进制流的, 它可以为每个请求分配一个序列号, 甚至可以请求拆分成不同的帧。 有了序列号服务就可以区分不同的请求和应答。
RPC框架与HTTP 参考资料 Protocol Buffer Language Guide
gRPC系列(一) 什么是RPC?
gRPC系列(二) 如何用Protobuf组织内容
gRPC系列(三) 如何借助HTTP2实现传输
Introductionto HTTP/2