【青训营笔记】 GRPC 笔记

86 阅读8分钟

1. 函数映射

函数映射是指在远程过程调用(RPC)中,将客户端请求的函数与服务器端的实现进行关联。当客户端调用一个远程函数时,它不会直接在本地执行,而是将请求发送到服务器端。服务器端通过函数映射,将请求映射到对应的函数实现,这样客户端的调用请求就可以触发服务器端的特定操作。

2. 数据转换字节流

在 RPC 通信中,需要将数据结构(如对象、参数等)转换为适合网络传输的格式。这一过程会将数据从内存中的表示形式转换为字节流(序列化),并通过网络传输到目标机器上。目标机器接收到字节流后,需要将其转换回内存中的数据结构(反序列化)。

3. 网络传输

网络传输指的是通过网络将序列化后的数据从客户端发送到服务器端,或将服务器端的响应发送回客户端。通常,这个过程使用成熟的网络协议,如 TCP 或 UDP。选择合适的协议取决于应用场景的需求,比如可靠性(TCP)或速度(UDP)。

RPC 组成的 5 个模块

  1. User(用户端) :这是客户端调用远程函数的地方,用户通过调用 RPC 方法来实现远程通信。
  2. User-Stub(用户桩) :客户端的桩(Stub)负责将用户的函数调用请求打包(序列化)为网络数据包,并通过网络发送给服务器端。它相当于一个代理,隐藏了底层的通信细节。
  3. RPC-Runtime(RPC 运行时) :在客户端和服务器端之间负责实际的网络传输任务。它处理底层的协议、数据传输、连接管理等功能。
  4. Server-Stub(服务器桩) :服务器端的桩(Stub)负责将接收到的网络数据解包(反序列化),并调用对应的服务器端函数。它将结果再打包返回给客户端。
  5. Server(服务器端) :服务器端实际处理请求的地方,接收从桩传来的请求并执行相应的操作。

IDL 文件

IDL(Interface Definition Language,接口定义语言)用于定义跨平台的对象和语言之间的通信接口。通过 IDL,可以在不同平台和不同编程语言之间进行通信。在 RPC 中,通过 IDL 文件生成对应的代码库,比如生成静态库或动态库,以便在客户端和服务器端使用相同的接口。

编解码(序列化和反序列化)

  • 序列化:将内存中的数据对象转换为字节序列,方便通过网络传输。这个过程会将复杂的数据结构(如对象、数组等)编码成标准的字节格式。
  • 反序列化:将接收到的字节序列重新转换回内存中的数据对象,恢复成可以被程序操作的数据结构。

通信协议

通信协议定义了网络传输数据的格式和规则。在 RPC 系统中,除了请求和响应的基本数据外,协议可能还包含一些额外的信息,如错误处理、元数据等,以保证数据传输的可靠性和完整性。常见的协议包括 HTTP、gRPC、Protobuf 等。

网络传输

网络传输是通过网络将编码后的数据进行传递。通常,这个过程使用 TCP 或 UDP 等协议,通过成熟的网络库进行实现。选择的协议会影响数据传输的可靠性、速度和开销。

RPC 的优点

  1. 单一职责:客户端只需要调用接口,而不需要关心底层的实现和通信细节,简化了开发和维护。
  2. 可拓展性强:RPC 系统容易扩展,通过增加新的服务端函数或改变接口定义,可以轻松添加新的功能。
  3. 故障隔离:RPC 服务之间相互独立,某个服务的故障不会直接影响其他服务,提高了系统的整体可靠性。

分层设计详细解释

1. 编解码层

编解码层负责在网络传输前对数据进行编码,将其转换为网络可以理解的格式;在接收数据时解码,将其恢复为可操作的内存数据结构。

编解码格式

编解码格式有两种主要类型:文本格式和二进制编码。

  • 文本格式:这些格式适合人类阅读和调试,易于理解,但效率较低。

    • JSON:JavaScript Object Notation,一种轻量级的数据交换格式,易于阅读和解析,但性能较慢。
    • XML:可扩展标记语言,结构化良好,适合复杂数据表示,但冗余较高。
    • CSV:逗号分隔值,用于简单的表格数据交换,但缺乏复杂数据结构的支持。
  • 二进制编码:这些格式更紧凑,适合高性能和跨语言通信。

    • Thrift:由 Apache 提供的高效跨语言 RPC 框架,支持多种编程语言。
    • BinaryProtocol:二进制协议,数据紧凑编码,减少冗余。
    • Protobuf:Google 的 Protocol Buffers,性能高且跨平台,结构化数据紧凑。
    • TLV:Tag-Length-Value 格式,常用于网络协议,通过 Tag 标识类型,Length 指定长度,Value 存储数据值。

选择编码方式的考虑因素

  • 兼容性:确保不同系统和平台之间可以无障碍通信。
  • 通用性:考虑是否能支持多种语言,适合广泛的应用场景。
  • 性能:选择高效的编码格式,以减少数据传输的开销。

2. 协议层

协议层定义了数据包的结构和传输方式。一个良好的协议能够有效地管理数据包,保证数据传输的完整性和安全性。

协议构造方法

  • 特殊结束符:通过定义特定的字符或字节序列作为数据包的结束符,以区分数据包的边界。
  • 变长协议:通过在数据包头部指定长度信息,使协议能够灵活处理不同长度的数据包。

示例协议字段解释

  1. LENGTH:数据包的大小,不包含自身的长度信息。可以用于确定整个数据包的大小。
  2. HEADER MAGIC:标识版本信息,用于协议解析时的快速验证,确保数据包的格式正确。
  3. SEQUENCE NUMBER:表示数据包的序列号(SeqID),用于多路复用,保证单连接内的数据包顺序递增。
  4. HEADER SIZE:头部的长度,从数据包的第 14 个字节开始计算,到消息体 (PAYLOAD) 之前结束。
  5. PROTOCOL ID:标识编解码方式,可能包含 Binary 和 Compact 两种协议方式。
  6. TRANSFORM ID:指示数据压缩方式,例如 zlib 或 snappy。
  7. INFO ID:包含一些自定义的元数据信息,用于特殊的通信需求。
  8. PAYLOAD:消息体,包含实际需要传输的数据。

3. 协议解析

协议解析负责对接收到的字节流进行检查和解码,保证数据包的完整性和有效性。

  • Peek:在不移除数据的情况下,检查数据包中的部分字段,用于协议的初步判断,比如 MagicNumber
  • MagicNumber:协议中一个特定的标识符,用于快速验证数据包是否符合预期格式。
  • PayloadCodec:对 PAYLOAD 数据部分的解码器,用于解码消息体的内容。
  • Decode:最终对接收到的数据包进行完整的解码,将其转化为可操作的数据结构。

4. 网络通信层

网络通信层负责在客户端和服务器之间传输数据,通过各种协议和 API 管理底层的网络通信。

网络通信层的关键组件

  • Sockets API:底层的网络接口,提供对 TCP/UDP 的直接访问,允许程序通过网络发送和接收数据。

  • 网络库

    :封装了底层的 Sockets API,提供更易于使用的 API,简化开发。例如 Go 的 net 库。

    • 连接管理和事件分发:处理连接的建立、维护和终止,分发收到的网络事件。
    • 协议支持:支持多种协议,如 TCP、UDP、UDS(Unix Domain Sockets)等。

image-20241115231651338

image-20241115231658184

网络通信层的功能

  1. 协议支持:支持多种协议,如 TCP 用于可靠传输,UDP 用于快速传输,UDS 用于本地进程间通信。

  2. 优雅退出和异常处理:在程序结束或发生异常时,能够正确关闭连接和释放资源。

  3. 性能优化

    :通过减少数据拷贝、使用应用层 buffer、对象池等手段提高性能。

    • 应用层 Buffer:减少数据在内存中的拷贝次数,提高效率。
    • 高性能定时器:用于定时任务的精确控制。
    • 对象池:重复利用对象,减少内存分配和垃圾回收的开销。