网络IO模型与RPC原理 | 青训营笔记

107 阅读8分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 11天,今天主要学习5中网络模型和RPC原理以及实现等。

1、 网络IO模型

IO操作本质

数据复制的过程中不会消耗CPU

  • 1 内存分为内核缓冲区和用户缓冲区
  • 2 用户的应用程序不能直接操作内核缓冲区,需要将数据从内核拷贝到用户才能使用
  • 3 而IO操作、网络请求加载到内存的数据一开始是放在内核缓冲区的

image.png

五中主要的IO模型

阻塞IO模型(BIO)

image.png 在同步阻塞IO模型中,程序在执行read函数之后相当于一次系统调用,在这段时间内,无论数据报没有准备好,那么读取数据的线程就会一直阻塞。直到返回成功后,应用程序开始处理用户空间的缓存区数据.主要分为两个阶段:

  • 等待数据就绪:网络IO就是等待远端数据陆续到达;磁盘IO就是等到磁盘数据从磁盘读取到内核缓冲区。
  • 数据复制: 用户空间的程序没有权限直接读取内核缓冲区的数据(操作系统处于安全的考虑),因此内核与需要把内核缓冲区的数据复制一份到进程缓冲区。

同步非阻塞IO(None Blocking IO)

image.png 在同步非阻塞IO模型中,会出现下面几种情况:

  • 在内核缓冲区没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。这样请求就不会阻塞。
  • 用户线程需要不断的发起IO系统调用,测试内核数据是否准备好。
  • 在内核缓冲区有数据的情况下,是阻塞的。直到内核缓冲区的数据全部复制到进程缓冲区,系统调用成功。

非阻塞IO在用户数据报还没准备好的时候,recvfrom系统调用不会阻塞,接着会继续进行下一轮的recvfrom系统调用看数据报有无准备好,周而复始,进程(线程)不断轮训,因此这是非常耗费CPU的。这种模型不是很常用,适合用在某台CPU专为某些功能准备的场合。

IO多路复用模型(IO Multiplexing)

在IO多路复用模型中通过select/epoll系统调用,单个应用程序的线程,可以不断轮询成百上千的socket连接当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作

image.png

select,poll,epoll都是IO多路复用的机制,I/O多路复用就是通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知应用程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

信号驱动IO模型

image.png

信号驱动IO模型在等待数据报期间是不会阻塞的,即用户进程(线程)发送一个sigaction系统调用后,此时立刻返回,并不会阻塞,然后用户进程(线程)继续执行;当数据报准备好时,此时内核就为该进程(线程)产生一个SIGIO信号,此时该进程(线程)就发生一次recvfrom系统调用将数据报从内核复制到用户空间,注意,这个阶段是阻塞的。

异步IO模型(Asynchronous IO)

异步IO模型也很好理解,即用户进程(线程)在等待数据报和数据报从内核拷贝到用户空间这两阶段都是非阻塞的,即用户进程(线程)发生一次系统调用后,立即返回,然后该用户进程(线程)继续往下执行。当内核把接收到数据报并把数据报拷贝到了用户空间后,此时再通知用户进程(线程)来处理用户空间的数据报。,也就是说,这一些列IO操作都交给了内核去处理了,用户进程无须同步阻塞,因此是异步非阻塞的。

image.png

后两种模型区别

异步IO模型跟信号驱动IO模型的区别在于当内核准备好数据报后,对于信号驱动IO模型,此时内核会通知用户进程说数据报准备好啦,你需要发起系统调用来将数据报从内核拷贝到用户空间,此过程是同步阻塞的;而对于异步IO模型,当数据报准备好时,内核不会再通知用户进程,而是自己默默将数据报从内核拷贝到用户空间后然后再通知用户进程说,数据已经拷贝到用户空间啦,你直接进行业务逻辑处理就行。

2、RPC原理及协议

本地函数调用

image.png

当执行main函数的时候,大概执行流程如下

image.png

假如服务是在不同的机器上,服务与服务之间就涉及到远程函数调用,即(RPC)。

image.png

  • 相比本地函数调用,RPC调用需要解决的问题

    • 函数映射
    • 数据转换成字节流
    • 网络传输
  • RPC的概念模型:User、User-Stub、RPC-Runtime、Server-Stub、Server

image.png

一次 RPC 的完整过程

image.png

IDL(Interface description language)文件

IDL通过一种中立的方式来描述接口,使得不同平台上运行的对象和用不同语言编写的程序可以以相互通信

生成代码

通过编译器工具将IDL文件转换成语言对应的静态库

编解码 从内存中表示到字节序列的转换为编码,反之为解码,通常叫做序列化与反序列化

通信协议 规范数据在网络中的传输内容和格式,除必须请求的响应数据之外,通常还有包括额外的元数据

网络传输 通常基于成熟的网络库进行TCP/UDP传输

RPC的好处

  • 单一职责,有利于分工协作和运维开发
  • 可扩展性强,资源使用率更优
  • 故障隔离,服务的整体可靠性更高

问题

RPC 带来的问题将由 RPC 框架来解决

-   服务宕机如何感知?
-   遇到网络异常应该如何应对?
-   请求量暴增怎么处理?

分层设计

以thrift为例

image.png

编解码层

  • 数据格式

    • 语言特定格式:例如 java.io.Serializable

    • 文本格式:例如 JSON、XML、CSV 等

    • 二进制编码:常见有 Thrift 的 BinaryProtocol,Protobuf,实现可以有多种形式,例如 TLV 编码 和 Varint 编码

如TLV编码 主要由tag、length、value组成

image.png 具体实例 image.png

image.png

编码选型考察点

-   兼容性
    支持自动增加新的字段,不影响老的服务,可以提高系统的灵活性
-   通用型
    支持跨平台、扩语言
-   [性能](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Falecthomas%2Fgo_serialization_benchmarks "https://github.com/alecthomas/go_serialization_benchmarks")

    -   空间开销
    -   时间开销
    即编码后的数据大小和编码耗费时长
    

协议层

  • 以 Thrift 的 THeader 协议为例

image.png

  • LENGTH 字段 32bits,包括数据包剩余部分的字节大小,不包含 LENGTH 自身长度

  • HEADER MAGIC 字段16bits,值为:0x1000,用于标识 协议版本信息,协议解析的时候可以快速校验

  • FLAGS 字段 16bits,为预留字段,暂未使用,默认值为 0x0000

  • SEQUENCE NUMBER 字段 32bits,表示数据包的 seqId,可用于多路复用,最好确保单个连接内递增

  • HEADER SIZE 字段 16bits,等于头部长度字节数/4,头部长度计算从第14个字节开始计算,一直到 PAYLOAD 前(备注:header 的最大长度为 64K)

  • PROTOCOL ID 字段 uint8 编码,取值有:

    • ProtocolIDBinary = 0
    • ProtocolIDCompact = 2
  • NUM TRANSFORMS 字段 uint8 编码,表示 TRANSFORM 个数

  • TRANSFORM ID 字段 uint8 编码,表示压缩方式

  • zlib or snappy

  • INFO ID 字段 uint8 编码,具体取值参考下文,用于传递一些定制的 meta 信息

  • PAYLOAD 消息内容

    协议解析

image.png

网络通信层

  • 阻塞 IO 下,耗费一个线程去阻塞在 read(fd) 去等待用足够多的数据可读并返回。
  • 非阻塞 IO 下,不停对所有 fds 轮询 read(fd) ,如果读取到 n <= 0 则下一个循环继续轮询。

第一种方式浪费线程(会占用内存和上下文切换开销),第二种方式浪费 CPU 做大量无效工作。而基于 IO 多路复用系统调用实现的 Poll 的意义在于将可读/可写状态通知和实际文件操作分开,并支持多个文件描述符通过一个系统调用监听以提升性能。
网络库的核心功能就是去同时监听大量的文件描述符的状态变化(通过操作系统调用),并对于不同状态变更,高效,安全地进行对应的文件操作。

关键指标

稳定性

  • 保障策略

    • 熔断 一个服务A调用服务B时,服务B的业务迈锻又调用了服务C,而服务C响应趣时了,由于服务B依裁服务C,C超时直接导数B的业务且辑─直等待,而这个时候服务A,频繁调用服务B,服务B的就可能会因为堆用大量的请求导致服务宕机,由此就导致了服务雪崩的问题

    • 限流 当调用端发送请求过来时,服务端在执行业务逻辑之前R执行检查限流逻辑,如果发现访问星过大并目超出了限流条件,就让服务端直接降级处理或者返回给调用方一个限流异常

    • 超时控制 当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,避免浪费资源

  • 请求成功率

    • 负载均衡
    • 重试
  • 长尾请求

    • BackupRequest

image.png

易用性

  • 开箱即用

    • 合理的默认参数选项、丰富的文档
  • 周边工具

    • 生成代码工具、脚手架工具

扩展性

  • Middleware:middleware 会被构造成一个有序调用链逐个执行,比如服务发现、路由、负载均衡、超时控制等
  • Option:作为初始化参数
  • 核心层是支持扩展的:编解码、协议、网络传输层
  • 代码生成工具也支持插件扩展

观测性

  • 三件套:Log、Metric 和 Tracing