1 RPC框架
1.1 RPC框架简介
RPC(Remote Procedure Call)是一种远程过程调用协议,它允许程序通过网络协议从一台机器上调用另一台机器上的函数或方法。RPC框架是一种用于实现RPC的开发框架,它为开发人员提供了一种方便的方式来实现RPC。
1.2 RPC框架流程
RPC框架的流程通常包括以下几个步骤:
- 服务端注册:服务端将自己提供的服务注册到RPC框架中,告诉框架它可以提供哪些服务以及如何访问这些服务。
- 客户端发起请求:客户端通过RPC框架向服务端发起请求,包括请求的服务名称、方法名称和参数等信息。
- RPC框架转发请求:RPC框架接收到客户端的请求后,将其转发给服务端。
- 服务端处理请求:服务端接收到RPC框架转发的请求后,根据请求的方法和参数进行处理,并返回结果给客户端。
- RPC框架转发响应:RPC框架接收到服务端的响应后,将其转发给客户端。
- 客户端接收响应:客户端接收到RPC框架转发的响应后,对响应进行处理,并继续执行后续的逻辑。
1.3 一次RPC的完整过程
- IDL(Interface description language)文件:IDL通过一种中立的方式来描述接口,使得在不同平台上运行的对象和用不同语言编写的程序可以相互通信
- 生成代码:通过编译器工具把IDL文件转换成语言对应的静态库
- 编解码:从内存中表示到字节序列的转换称为编码,反之为解码,也常叫做序列化和反序列化
- 通信协议:规范了数据在网络中的传输内容和格式。除必须的请求/响应数据外,通常还会包含额外的元数据
- 网络传输:通常基于成熟的网络库走TCP/UDP传输
1.4 RPC框架优缺点
优点:
- 可以方便地实现跨平台的远程调用,提高代码的可移植性。
- 可以提高程序的可扩展性和灵活性,资源使用率更优,程序可以快速地适应新的需求。
- 可以减少网络传输的开销,提高网络传输的效率。
- 单一职责,有利于分工协作和运维开发
- 故障隔离,服务的整体可靠性更高
缺点:
- 由于需要使用RPC框架,因此会增加程序的复杂度和开发难度。
- RPC框架的性能通常不如直接使用网络协议进行通信。
- RPC框架的安全性较差,容易受到攻击。
2 RPC框架分层设计
RPC框架通常采用分层设计,将不同的功能分别放在不同的层中,以实现更好的可扩展性和可维护性。下面是RPC框架的常见分层设计:
2.1 编解码层
编解码层负责将RPC请求和响应进行编码和解码,以便在网络上传输。常见的编解码协议包括JSON、XML、Thrift等。
- JSON是一种轻量级的数据交换格式,易于阅读和理解,但是编码和解码效率较低。
- XML是一种标准的数据交换格式,支持复杂的数据结构和元数据,但是编码和解码效率较低。
- Thrift是一种开源的RPC编解码协议,它采用了C++语言进行实现,支持多种编程语言的编写,同时具有高效、可扩展性好等特点。
编解码层的作用是将RPC请求和响应转换成字节流,以便在网络上传输。在发送RPC请求时,需要将请求对象按照编码协议进行编码,生成字节流;在接收RPC响应时,需要将字节流按照编码协议进行解码,生成响应对象。
2.1.1 生成代码
TLV编码:
- Tag:标签,可以理解为类型
-
- Lenght:长度
- Value: 值,Value 也可以是个TILV结构
2.1.3 选型
- 兼容性:支持自动增加新的字段,而不影响老的服务,这将提高系统的灵活度
- 通用性:支持跨平台、跨语言
- 性能:从空间和时间两个维度来考虑,也就是编码后数据大小和编码耗费时长
2.2 协议层
协议层负责处理RPC请求和响应的传输协议,包括传输方式、数据格式等。协议层的作用是将RPC请求和响应按照特定的传输协议进行传输。在发送RPC请求时,需要将请求对象按照协议规定的格式进行编码,生成字节流;在接收RPC响应时,需要将字节流按照协议规定的格式进行解码,生成响应对象。
常见的协议包括HTTP、gRPC、Thrift等。
- HTTP是一种常用的网络传输协议,支持多种数据格式,易于使用,但是不适合传输大量数据。
- gRPC是一种基于HTTP/2协议的RPC传输协议,支持多种数据格式,传输效率高,但是实现较为复杂。
- Thrift是一种开源的RPC传输协议,支持多种数据格式,传输效率高,同时具有高效、可扩展性好等特点。
2.2.1 概念
- 特殊结束符:一个特殊字符作为每个协议单元结束的标示
- 变长协议:以定长加不定长的部分组成,其中定长的部分需要描述不定长的内容长度
2.2.2 协议构造
- LENGTH:数据包大小,不包含自身
- HEADER MAGlC:标识版本信息,协议解析时候快速校验
- SEQUENCE NUMBER:表示数据包的seqlD ,可用于多路复用,单连接内递增
- HEADER SIZE:头部长度,从第14个字节开始计算一直到PAYLOAD前
- PROTOCOL ID:编解码方式,有Binary和Compact 两种
- TRANSFORM ID:压缩方式,如zlib 和snappy
- INFO ID:传递一些定制的meta信息
- PAYLOAD:消息体
2.3 网络通信层
网络通信层负责实现网络通信,包括TCP/IP、UDP等。网络通信层的作用是将RPC请求和响应通过网络进行传输。在发送RPC请求时,需要将请求对象按照协议规定的格式进行编码,生成字节流;在接收RPC响应时,需要将字节流按照协议规定的格式进行解码,生成响应对象。
通常会使用一些网络库和框架,如Socket、libevent、libcurl等。
- Socket是一种常用的网络通信库,支持TCP/IP协议,易于使用,但是实现较为复杂。
- libevent是一种高效的网络事件处理库,支持多线程和多进程,但是使用较为复杂。
- libcurl是一种常用的网络传输库,支持多种网络协议和数据格式,易于使用,但是实现较为复杂。
2.3.1 Sockets API
套接字编程中的客户端必须知道两个信息:服务器的IP地址,以及端口号。
socket创建一个套接字,bind将一个套接字绑定到一个地址上。listen监听进来的准接,backlog指定挂起的连接队列长度,当客户端连接时,服务器可能正在处理其他逻辑而未调用accept接受连接,此时会导致该连接被挂起,内核维护挂起的连接队列, backlog指定这个队列的长度、accept函数从队列中取出连接请求并接收它,然后这个连接就从挂起队列移除。如果队列夫满,客户端调用connect马上成功,如果满了可能会阻塞等待队列末未。Linux的backlog默认是128,通常情况下,我们也指定为128即可。
connect客户端向服务器发起连接,accept接收一个连接请求,如果没有连接则会一直阻塞直到有连接进来。得到客户端的fd之后,就可以调用read, write和客户端通讯,读写方式和其他I/O相似
- read 从fd读数据,socket默认是阻塞模式的,如果对方没有写数据,read会一直阻塞着。
- write写fd写数据,socket默认是阻塞模式的,如果对方没有写数据,write会一直阻塞着。
socket关闭套接字,当另一端socket关闭后,这一端读写的情况:
- 尝试去读会得到—个EOF,并返回0。
- 尝试去写会触发一个SIGPIPE信号,并返回-1和error=EPIPE, SIGPIPE的默认行为是终止程序,所以通常应该忽略这个信号,避免程序终止。如果这—端不去读写,那么没有办法知道对端的socket关闭。
2.3.2 网络库
- 提供易用API:封装底层Socket APl连接管理和事件分发
- 功能:协议支持:tcp、udp 和uds 等优雅退出、异常处理等
- 性能:应用层 buffer 减少copy高性能定时器、对象池等
2.4 Apache Thrift设计案例
总的来说,RPC框架的分层设计可以将不同的功能分别放在不同的层中,以实现更好的可扩展性和可维护性。在实现RPC框架时,可以根据实际需求选择不同的编解码协议、传输协议和网络通信库,以满足不同的需求。同时,RPC框架还需要具有高效、可扩展性好等特点,以适用于大规模分布式系统。
3 总结
本文首先介绍了RPC框架及其完整过程,简要分析RPC框架的优缺点,后详细介绍RPC框架分层设计的原理与方法,主要包括编解码层、协议层和网络通信层设计要求和注意事项,最后通过Apache Thrift设计案例总结RPC分层设计的方法和框架设计的要求。
本文为个人见解,如有错误欢迎指正!