[TOC]
RPC
基本概念
摘自:https://www.jianshu.com/p/7d6853140e13
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。
远程过程调用
上述操作的过程中,如果addAge()这个方法在服务端,执行函数的函数体在远程机器上,如何告诉机器需要调用这个方法呢?
- 1.首先客户端需要告诉服务器,需要调用的函数,这里函数和进程ID存在一个映射,客户端远程调用时,需要查一下函数,找到对应的ID,然后执行函数代码。
- 2.客户端需要把本地参数传给远程函数,本地调用的过程中,直接压栈即可,但是在远程调用过程中不再同一个内存里,无法直接传递函数的参数,因此需要客户端把参数转换成字节流,传给服务端,然后服务端将字节流转换成自身能读取的格式,是一个序列化和反序列化的过程。
- 3.数据准备好了之后,如何进行传输?网络传输层需要把调用的ID和序列化后的参数传给服务端,然后把计算好的结果序列化传给客户端,因此TCP层即可完成上述过程,gRPC中采用的是HTTP2协议。
总结
Client端 :Student student = Call(ServerAddr, addAge, student)
将这个调用映射为Call ID。
将Call ID,student(params)序列化,以二进制形式打包
把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
等待服务器返回结果
如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新
Server端
在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map
callIdMap 等待请求
得到一个请求后,将其数据包反序列化,得到Call ID
通过在callIdMap中查找,得到相应的函数指针
将student(params)反序列化后,在本地调用addAge()函数,得到结果
将student结果序列化后通过网络返回给Client
RPC架构
一个完整的RPC架构里面包含了四个核心的组件,分别是Client ,Server,Client Stub以及Server Stub。分别说说这几个组件:
- 客户端(Client),服务的调用方。
- 服务端(Server),真正的服务提供者。
- Client Stub,存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
- Server Stub,接收客户端发送过来的消息,将消息解包,并调用本地的方法。
为什么要用RPC
RPC效率更高,在传输例如图像这种大的字节流文件时速度比Http更快,主要原因有以下三点
- 1.减少传输量。
- 2.简化协议。
- 3.用长连接,不再每一个请求都重新走三次握手流程
RPC服务更适合用做线上分布式服务之间互相使用的通信协议。而HTTP服务开发迭代会更快。
gRPC
常见的RPC框架有:
- gRPC。谷歌出品
- Thrift。Apache出品
- Dubbo。阿里出品,也是一个微服务框架
gRPC的4点特性:
- 使用Protocal Buffers这个强大的结构数据序列化工具
- grpc可以跨语言使用
- 安装简单,扩展方便(用该框架每秒可达到百万个RPC)
- 基于HTTP2协议
gRPC使用流程:
- 定义标准的proto文件
- 生成标准代码
- 服务端使用生成的代码提供服务
- 客户端使用生成的代码调用服务
protocol buffers是什么
谷歌开源的一种结构数据序列化的工具,比方说JSON、XML也是结构数据序列化的工具,不同的是,
- Protocol Buffers序列化后的数据是不可读的,因为是二进制流
- 使用Protocol Buffer需要事先定义数据的格式(.proto 协议文件),还原一个序列化之后的数据需要使用到这个数据格式
- Protocol Buffer 比 XML、JSON快很多,因为是基于二进制流,比字符串更省带宽,传输速度快
基于python的代码实践
目录结构:
|
|
example.proto
|
|
编译 proto 文件生成服务接口
|
|
server.py
|
|
client.py
|
|
调试gRPC
rest api是可用curl和postman这样的工具来调试的,grpc也有类似的工具,对应的分别是grpcurl和grpcui(均基于go语言安装),其中grpcui类似于postman,有web页面帮助调试,用户友好。