1. RPC基础概念
1.1 RPC
远程过程调用(Remote Procedure Call, RPC),一种进程间通信的模式,允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。
1.2 微服务
微服务是一种开发软件的架构和组织方法,其中软件由通过明确定义的API进行通信的小型独立服务组成,这些服务围绕业务构建,每项服务执行单一功能。
采用微服务架构后,可以将业务组件封装为中台服务,通过RPC进行远程调用,有着方便复用、系统架构清晰的好处。此外,由于各组件独立运行的性质,可以针对各项服务单独进行更新、部署和扩展,以满足对应用程序特定功能的需求。
由于服务间通信频繁,需要采用轻量级的通信。随着微服务架构的流行,RPC框架渐渐地成为服务框架的一个重要部分。
1.3 Protocol Buffer (Protobuf)
RPC对通信性能(例如信息的编解码)有着很高的要求。Protocol Buffer,简称Protobuf,是Google开源的一个语言无关、平台无关的通信协议,有着跨语言、跨平台、高性能的优点。
1.4 gRPC
Google开源的一个高性能的RPC框架,使用基于protobuf的二进制协议进行通信,有着高性能、支持多语言、成熟且广泛使用等优点。
参考
https://zh.wikipedia.org/zh-cn/%E9%81%A0%E7%A8%8B%E9%81%8E%E7%A8%8B%E8%AA%BF%E7%94%A8
https://aws.amazon.com/cn/microservices/
https://yuyy.info/?p=1911
2. RPC与HTTP(或RESTful API) 比较?
- RESTful API需要有确定的URL,不是所有场景下操作的对象都有URL,而RPC类似函数调用的形式可以覆盖这些场景。
- RPC在报文大小和编解码速度上都更占优势。RPC往往使用更加高效的编解码协议,例如gRPC用的protobuf是自定义的二进制报文;RESTful API基于HTTP,往往是用json或者XML,虽然可读性更高但是数据包更大,解析更需时间,此外报文的头部字段也更长,效率较低。
- RPC基于TCP协议,也可以基于HTTP协议。HTTP自然是基于HTTP
- RPC往往是服务内部调用,RESTful API往往面向用户。
参考
3. RPC架构组件
一个基本的RPC架构里面应该至少包含以下4个组件: 组件 |
说明 |
---|---|
客户端 Client | 服务调用方(服务消费者) |
客户端存根 Client Stub | 存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端 |
服务端存根 Server Stub | 接收客户端发送过来的请求消息并进行解包,然后再调用本地服务进行处理 |
服务端 Server | 服务的真正提供者 |
具体调用过程:
- 服务消费者(client客户端)通过调用本地服务的方式调用需要消费的服务;
- 客户端存根(client stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体;
- 客户端存根(client stub)找到远程的服务地址,并且将消息通过网络发送给服务端;
- 服务端存根(server stub)收到消息后进行解码(反序列化操作);
- 服务端存根(server stub)根据解码结果调用本地的服务进行相关处理;
- 本地服务执行具体业务逻辑并将处理结果返回给服务端存根(server stub);
- 服务端存根(server stub)将返回结果重新打包成消息(序列化)并通过网络发送至消费方;
- 客户端存根(client stub)接收到消息,并进行解码(反序列化);
- 服务消费方得到最终结果;
而RPC框架的实现目标则是将上面的第2-10步完好地封装起来,也就是把调用、编码/解码的过程给封装起来,让用户感觉上像调用本地服务一样的调用远程服务。
参考
https://juejin.cn/post/7055613735020265479
4. Protobuf定义服务
使用protobuf定义服务的包含以下几个步骤:首先通过一个.proto文件来定义服务,包含gRPC service、request以及response的类型;其次通过protobuf编译器protoc
生成对应高级语言版本的服务类文件和消息类文件,
4.1 .proto文件定义服务
.proto文件包含三个部分:定义服务、请求类型和响应类型。定义服务的service
代码块如下所示:
service {
// ...
}
在service
块中需要定义服务类型,gRPC支持4种服务类型:
4.1.1 简单RPC (simple RPC)
简单RPC与通常的函数调用类似,客户端使用存根(stub)发送请求到服务器并等待响应返回。
rpc GetFeature(Point) returns (Feature) {}
4.1.2 服务端流式RPC (server-side streaming RPC)
服务端流式RPC在定义时需要在响应类型前插入stream
关键字。在这种服务类型下,客户端发送请求到服务器,得到一个流作为返回,客户端通过读取流获得一系列消息,直到流内消息全部读完后结束。
rpc ListFeatures(Rectangle) returns (stream Feature) {}
4.1.3 客户端流式RPC (client-side streaming RPC)
客户端流式RPC在定义时需要在请求类型前插入stream
关键字。在这种服务类型下,客户端使用流的方式发送一系列消息到服务器,完成发送后等待响应返回。客户端通过读取流获得一系列消息,直到流内消息全部读完后结束。
rpc RecordRoute(stream Point) returns (RouteSummary) {}
4.1.4 双向流式RPC (bidirectional streaming RPC)
双向流式RPC在定义时需要在请求和响应类型前都插入stream
关键字。在这种服务类型下,两个流独立运行,客户端与服务端的读写顺序没有固定要求,例如服务器可以在收到客户端发来的所有消息后再开始响应,也可以读一条消息再响应一条消息。流中的消息顺序会被记录。
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
4.2 .proto文件定义消息
此外,proto文件还包括在定义服务的请求和响应时出现的所有消息类型,例如在上文中出现的Point
类型的定义如下:
message Point {
int32 latitude = 1;
int32 longitude = 2;
}