网络通信:RPC 框架在网络通信上更倾向于哪种网络 IO 模型?

简介: 本讲深入探讨RPC框架中的网络通信机制,重点分析常用网络IO模型。由于RPC调用本质是服务消费者与提供者间的网络数据交换,因此高效IO模型至关重要。常见的IO模型有BIO、NIO、IO多路复用和AIO,其中IO多路复用因支持高并发、节省资源,成为RPC框架首选,如Netty基于Reactor模式实现,广泛应用于Java体系。

04 | 网络通信:RPC 框架在网络通信上更倾向于哪种网络 IO 模型?
通过上一讲,我们知道由于网络传输的数据都是二进制数据,所以我们要传递对象,就必须将对象进行序列化,而 RPC 框架在序列化的选择上,我们更关注序列化协议的安全性、通用性、兼容性,其次才关注序列化协议的性能、效率、空间开销。承接上一讲,这一讲,我要专门讲解下 RPC 框架中的网络通信,这也是我们在开篇中就强调过的重要内容。
那么网络通信在 RPC 调用中起到什么作用呢?
我在 第 01 讲 中讲过,RPC 是解决进程间通信的一种方式。一次 RPC 调用,本质就是服务消费者与服务提供者间的一次网络信息交换的过程。服务调用者通过网络 IO 发送一条请求消息,服务提供者接收并解析,处理完相关的业务逻辑之后,再发送一条响应消息给服务调用者,服务调用者接收并解析响应消息,处理完相关的响应逻辑,一次 RPC 调用便结束了。可以说,网络通信是整个 RPC 调用流程的基础。
常见的网络 IO 模型
那说到网络通信,就不得不提一下网络 IO 模型。为什么要讲网络 IO 模型呢?因为所谓的两台 PC 机之间的网络通信,实际上就是两台 PC 机对网络 IO 的操作。
常见的网络 IO 模型分为四种:同步阻塞 IO(BIO)、同步非阻塞 IO(NIO)、IO 多路复用和异步非阻塞 IO(AIO)。在这四种 IO 模型中,只有 AIO 为异步 IO,其他都是同步 IO。
其中,最常用的就是同步阻塞 IO 和 IO 多路复用,这一点通过了解它们的机制,你会 get 到。至于其他两种 IO 模型,因为不常用,则不作为本讲的重点,有兴趣的话我们可以在留言区中讨论。
阻塞 IO(blocking IO)
同步阻塞 IO 是最简单、最常见的 IO 模型,在 Linux 中,默认情况下所有的 socket 都是 blocking 的,先看下操作流程。
首先,应用进程发起 IO 系统调用后,应用进程被阻塞,转到内核空间处理。之后,内核开始等待数据,等待到数据之后,再将内核中的数据拷贝到用户内存中,整个 IO 处理完毕后返回进程。最后应用的进程解除阻塞状态,运行业务逻辑。
这里我们可以看到,系统内核处理 IO 操作分为两个阶段——等待数据和拷贝数据。而在这两个阶段中,应用进程中 IO 操作的线程会一直都处于阻塞状态,如果是基于 Java 多线程开发,那么每一个 IO 操作都要占用线程,直至 IO 操作结束。
这个流程就好比我们去餐厅吃饭,我们到达餐厅,向服务员点餐,之后要一直在餐厅等待后厨将菜做好,然后服务员会将菜端给我们,我们才能享用。
IO 多路复用(IO multiplexing)
多路复用 IO 是在高并发场景中使用最为广泛的一种 IO 模型,如 Java 的 NIO、Redis、Nginx 的底层实现就是此类 IO 模型的应用,经典的 Reactor 模式也是基于此类 IO 模型。
那么什么是 IO 多路复用呢?通过字面上的理解,多路就是指多个通道,也就是多个网络连接的 IO,而复用就是指多个通道复用在一个复用器上。
多个网络连接的 IO 可以注册到一个复用器(select)上,当用户进程调用了 select,那么整个进程会被阻塞。同时,内核会「监视」所有 select 负责的 socket,当任何一个 socket 中的数据准备好了,select 就会返回。这个时候用户进程再调用 read 操作,将数据从内核中拷贝到用户进程。
这里我们可以看到,当用户进程发起了 select 调用,进程会被阻塞,当发现该 select 负责的 socket 有准备好的数据时才返回,之后才发起一次 read,整个流程要比阻塞 IO 要复杂,似乎也更浪费性能。但它最大的优势在于,用户可以在一个线程内同时处理多个 socket 的 IO 请求。用户可以注册多个 socket,然后不断地调用 select 读取被激活的 socket,即可达到在同一个线程内同时处理多个 IO 请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
同样好比我们去餐厅吃饭,这次我们是几个人一起去的,我们专门留了一个人在餐厅排号等位,其他人就去逛街了,等排号的朋友通知我们可以吃饭了,我们就直接去享用了。
为什么说阻塞 IO 和 IO 多路复用最为常用?
了解完二者的机制,我们就可以回到起初的问题了——我为什么说阻塞 IO 和 IO 多路复用最为常用。对比这四种网络 IO 模型:阻塞 IO、非阻塞 IO、IO 多路复用、异步 IO。实际在网络 IO 的应用上,需要的是 系统内核的支持以及编程语言的支持。
在系统内核的支持上,现在大多数系统内核都会支持阻塞 IO、非阻塞 IO 和 IO 多路复用,但像信号驱动 IO、异步 IO,只有高版本的 Linux 系统内核才会支持。
在编程语言上,无论 C++ 还是 Java,在高性能的网络编程框架的编写上,大多数都是基于 Reactor 模式,其中最为典型的便是 Java 的 Netty 框架,而 Reactor 模式是基于 IO 多路复用的。当然,在非高并发场景下,同步阻塞 IO 是最为常见的。
综合来讲,在这四种常用的 IO 模型中,应用最多的、系统内核与编程语言支持最为完善的,便是阻塞 IO 和 IO 多路复用。这两种 IO 模型,已经可以满足绝大多数网络 IO 的应用场景。
RPC 框架在网络通信上倾向选择哪种网络 IO 模型?
讲完了这两种最常用的网络 IO 模型,我们可以看看它们都适合什么样的场景。
IO 多路复用更适合高并发的场景,可以用较少的进程(线程)处理较多的 socket 的 IO 请求,但使用难度比较高。当然高级的编程语言支持得还是比较好的,比如 Java 语言有很多的开源框架对 Java 原生 API 做了封装,如 Netty 框架,使用非常简便;而 GO 语言,语言本身对 IO 多路复用的封装就已经很简洁了。
而阻塞 IO 与 IO 多路复用相比,阻塞 IO 每处理一个 socket 的 IO 请求都会阻塞进程(线程),但使用难度较低。在并发量较低、业务逻辑只需要同步进行 IO 操作的场景下,阻塞 IO 已经满足了需求,并且不需要发起 select 调用,开销上还要比 IO 多路复用低。
RPC 调用在大多数的情况下,是一个高并发调用的场景,考虑到系统内核的支持、编程语言的支持以及 IO 模型本身的特点,在 RPC 框架的实现中,在网络通信的处理上,我们会选择 IO 多路复用的方式。开发语言的网络通信框架的选型上,我们最优的选择是基于 Reactor 模式实现的框架,如 Java 语言,首选的框架便是 Netty 框架(Java 还有很多其他 NIO 框架,但目前 Netty 应用得最为广泛),并且在 Linux 环境下,也要开启 epoll 来提升系统性能(Windows 环境下是无法开启 epoll 的,因为系统内核不支持)。
了解完以上内容,我们可以继续看这样一个关键问题—零拷贝。在我们应用的过程中,他是非常重要的。
什么是零拷贝?
刚才讲阻塞 IO 的时候我讲到,系统内核处理 IO 操作分为两个阶段——等待数据和拷贝数据。
等待数据,就是系统内核在等待网卡接收到数据后,把数据写到内核中;
而拷贝数据,就是系统内核在获取到数据后,将数据拷贝到用户进程的空间中。

相关文章
|
1天前
|
负载均衡 算法 网络协议
负载均衡:节点负载差距这么大,为什么收到的流量还一样?
本文探讨RPC框架中的自适应负载均衡机制。针对传统权重调节滞后问题,提出通过实时采集节点CPU、内存、请求耗时等指标,结合权重算法动态打分,自动调整节点最终权重,实现流量智能分配,提升系统稳定性与响应效率。
|
1天前
|
存储 XML JSON
序列化:对象怎么在网络中传输?
本课讲解RPC框架中的序列化机制,介绍JDK原生、JSON、Hessian、Protobuf等常用序列化方式,分析其性能、体积、兼容性与安全性。重点强调在选型时应优先考虑通用性、兼容性与安全性,其次才是性能与空间开销。同时指出使用中常见问题:对象过繁、过大、继承复杂等,建议入参返回值尽量简洁、扁平、使用原生类型,以提升RPC调用的稳定性与效率。
|
1天前
|
Linux Shell 虚拟化
-Docker网络
Docker网络通过虚拟网桥docker0实现容器间通信与隔离。默认采用bridge模式,为容器分配IP并连接至docker0网桥,支持通过服务名互访。借助Linux namespace和cgroup特性实现网络隔离,提供bridge、host、none、container四种网络模式,灵活满足不同场景需求。
|
1天前
|
关系型数据库 MySQL 数据库
-Docker安装Mysql
本教程介绍Docker安装MySQL 5.7的完整流程,涵盖单机部署与主从复制配置。包括解决中文乱码、数据持久化卷映射,以及主从同步设置,确保数据安全与高可用,适用于生产环境部署参考。
|
1天前
|
JSON Shell Linux
Registry搭建
本文介绍如何搭建Docker私有镜像仓库(Registry)。通过拉取官方Registry镜像并运行,配置insecure-registries支持HTTP,重启Docker服务后,可为镜像打标签并推送到私仓,再通过curl验证镜像目录,最后实现私仓的拉取与使用,完成私有化管理。
|
1天前
|
负载均衡 安全 数据库
异常重试:在约定时间内安全可靠地重试
本节讲解RPC框架中的异常重试机制,重点探讨如何在超时控制、节点避让和异常判定的基础上实现安全可靠的重试。需确保业务逻辑幂等,设置重试白名单,并在每次重试前重置超时时间,避免耗时超标,提升系统容错性与稳定性。(239字)
|
1天前
|
存储 负载均衡 测试技术
路由策略:怎么让请求按照设定的规则发到不同的节点上?
本文深入讲解RPC中的路由策略,如何通过IP路由和参数路由实现灰度发布。在服务集群中,借助路由规则筛选请求节点,可精准控制流量,降低上线风险。参数路由基于请求内容打标分流,确保同一业务对象请求始终调用相同版本实例,实现平滑升级与细粒度流量治理。(238字)
|
1天前
|
监控 负载均衡 网络协议
健康检测:这个节点都挂了,为啥还要疯狂发请求?
本文探讨RPC框架中服务健康检测的挑战与优化。通过真实案例揭示:当节点网络异常、心跳间歇失败时,仅依赖心跳机制易导致“半死不活”节点持续接收请求。提出结合业务请求可用率(成功次数/总调用次数)动态评估节点状态,弥补传统心跳机制的不足,实现更精准的健康判断,提升系统稳定性与可用性。
|
1天前
|
存储 缓存 负载均衡
服务发现:到底是要 CP 还是 AP?
本文探讨RPC框架中服务发现的CP与AP选择问题。在超大规模集群下,基于ZooKeeper的强一致(CP)方案因性能瓶颈易导致宕机,而最终一致(AP)方案通过消息总线实现数据同步,兼顾性能与稳定性,更适用于高可用、低延迟的服务发现场景。
|
1天前
|
网络协议 算法 前端开发
架构设计:设计一个灵活的 RPC 框架
本讲深入讲解如何设计一个灵活的 RPC 框架,从传输、协议、引导到服务发现与治理,构建四层架构体系,并引入插件化设计提升可扩展性,实现高内聚、低耦合、易维护的微内核架构,助力系统应对持续变化的业务需求。(238字)