网络IO模型

前言

在日常开发工作中,经常会看到BIO、NIO、AIO这些名词,但是没有去深入研究过。本文作为一篇个人学习的笔记,有不足之处,还请指正。

 

1. Linux网络I/O模型简介

大多网络IO讨论的背景是Linux环境下,因此本文的介绍也主要围绕Linux下的网络IO讲述。

 

1.1 整体概括

此处引用网络IO演变过程文章的一张图,是我看过比较易懂的同步与异步、阻塞与非阻塞的图解。

 

 

1.2 Linux I/O模型

根据UNIX网络编程对I/O模型的分类,UNIX提供了5种I/O模型,分别如下:

  • 阻塞I/O模型:以套接字接口为例来说明该模型:在进程空间中调用recvfrom,其系统调用直到数据包到达且被复制到应用进程的缓冲区中或者发生错误时才返回,在此期间一直会等待,进程在从调用recvfrom开始到它返回的整段时间内都是被阻塞的,因此被称为阻塞I/O模型。一般使用阻塞I/O时,都需要配置多线程来使用,每个连接一个单独的线程进行处理。

preview

 

  • 非阻塞I/O模型:recvfrom从应用层到内核的时候,如果该缓冲区没有数据的话,就直接返一个EWOULDBLOCK错误,一般都是对非阻塞I/O模型进行轮询检查这个状态,看内核是不是有数据到来。非阻塞I/O解决了阻塞I/O每个连接一个线程处理的问题,但也伴随着一个问题:需要用户多次发起系统调用,频繁的系统调用是比较消耗系统资源的。

preview

 

  • I/O复用模型:Linux提供select/poll,进程通过将一个或多个fd传递给select或poll系统调用,阻塞在select操作上,这样select/poll可以帮我们侦测多个fd是否处于就绪状态。select/poll是顺序扫描fd是否就绪,而且支持的fd数量有限,默认是1024个,可以通过修改配置来改变,但终究是有限个。此外,select/poll在管理收到的连接时,会频繁的从用户态拷贝到内核态,比较消耗资源。因此后面出现了改进的epoll,支持一个进程打开的socket描述符(FD)不受限制(仅受限于操作系统的最大文件句柄数)。epoll是通过内核和用户空间mmap同一块内存来加速内核与用户空间的消息传递。此外,epoll每次调用时不再线性扫描全部的集合,而是只会对“活跃”的socket进行操作——这是因为在内核实现中,epoll是根据每个fd上面的callback函数实现的,只有“活跃”的socket才会去主动调用callback函数。epoll 对文件描述符的操作有两种模式:LT(level trigger)和 ET(edge trigger)。LT 模式是默认模式,当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。 ET 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知此事件。

 

  • 信号驱动I/O模型:首先开启套接口信号驱动I/O功能,并通过系统调用sigaction执行一个信号处理函数(此系统调用立即返回,进程继续工作,它是非阻塞的)。当数据准备就绪时,就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据,并通知主循环函数处理数据。

image

 

  • 异步I/O:告知内核启动某个操作,并让内核在整个操作完成后(包括将数据从内核复制到用户的缓冲区)通知我们。与信号驱动模型的主要区别是:信号驱动I/O由内核通知我们何时可以开始一个I/O操作;异步I/O模型由内核通知我们I/O操作何时已经完成。

image

 

2. 网络IO的各种模型

目前 reactor 模型有以下几种实现方案:

1. 单 reactor 单线程模型
2. 单 reactor 多线程模型
3. multi-reactor 多线程模型
4. multi-reactor 多进程模型

下文网络模型的图,均摘自高性能IO模型分析

 

2.1 单 reactor 单线程模型

此种模型,通常是只有一个 epoll 对象,所有的接收客户端连接客户端读取客户端写入操作都包含在一个线程内。该种模型也有一些中间件在用,比如 redis

 

2.2 单 reactor 多线程模型

该模型主要是通过将,前面的模型进行改造,将读写的业务逻辑交给具体的线程池来实现,这样可以显示 reactor 线程对 IO 的响应,以此提升系统性能。

在工作者线程池模式中,虽然非I/O操作交给了线程池来处理,但是所有的I/O操作依然由Reactor单线程执行,在高负载、高并发或大数据量的应用场景,依然较容易成为瓶颈。所以,对于Reactor的优化,又产生出下面的多线程模式。

 

2.3 Reactor模式-多线程模式

在这种模型中,主要分为两个部分:mainReactor、subReactors。 mainReactor 主要负责接收客户端的连接,然后将建立的客户端连接通过负载均衡的方式分发给 subReactors,subReactors 来负责具体的每个连接的读写,对于非 IO 的操作,依然交给工作线程池去做,对逻辑进行解耦。

mainReactor 对应 Netty 中配置的 BossGroup 线程组,主要负责接受客户端连接的建立。一般只暴露一个服务端口,BossGroup 线程组一般一个线程工作即可 subReactor 对应 Netty 中配置的 WorkerGroup 线程组,BossGroup 线程组接受并建立完客户端的连接后,将网络 socket 转交给 WorkerGroup 线程组,然后在 WorkerGroup 线程组内选择一个线程,进行 I/O 的处理。WorkerGroup 线程组主要处理 I/O,一般设置 2*CPU 核数个线程。

 

3. 主流的中间件采用的网络模型

中间件epoll触发方式Reactor模型
RedisLT单Reactor模型
SkynetLT单Reactor模型
MemcachedLT多Reactor模型(多线程)
NginxET多Reactor模型(多进程)

 

 

 

 

 

 

4. 参考资料

  1. 网络IO演变过程
  2. 高性能IO模型分析-Reactor模式和Proactor模式
  3. 《Netty权威指南》第2版
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值