【go基础】16.I/O模型与网络轮询器netpoller_go中的多路io复用模型-CSDN博客
字节开源的netPoll多路复用器源码解析-CSDN博客
一、几种常见的IO模型
1. 阻塞I/O
(1) 解释:
用户调用如accept、read等系统调用,向内核发起I/O请求后,应用程序会从用户态陷入内核态。
内核会检查文件描述符fd是否就绪,如果没有数据准备就绪,内核就会挂起(阻塞)当前的线程。
当fd中数据就绪后,操作系统内核会将数据从内核空间拷贝到进程空间,并交回控制权。
(2) 缺点:
一个goroutine同一时刻只能处理一个流,无法解决多个IO请求问题
2. 非阻塞I/O
(1) 解释:
执行read和write 等I/O操作会立刻返回,然后应用程序不停轮询。读取时如果没有数据,会返回
EAGAIN或EWOULDBLOCK错误码。
(2) 缺点:
占用cpu资源,不停做无用的轮询操作
3. I/O多路复用模型
(1) 解释:
一个进程/线程处理多个I/O,内核同时监视多个文件描述符,一旦某个描述符就绪,通知程序进行相应的读写操作;如果没有文件句柄就绪,就阻塞应用程序,交出CPU。
(2) 缺点:无,基本符合需求。
4. 三种IO模型对比
目前epoll是linux大规模并发网络应用程序的首选。
大部分高性能的网络服务器使用的I/O复用模型均是epoll,比如nginx、redis、memcached等。
二、几种常见的IO模型 使用方法
以下模型的 IO 操作都是 使用阻塞的机制。
1、多线程
(1) 解释:
每accept到一个客户端后,都启动一个新的线程。存在多个线程。
(2) 优点: 简单。
(3) 缺点:
a. 占用内存资源,每个线程需要2M的栈空间;需要的线程很多,且频繁切换。
b. 长连接需要保持连接状态(心跳/健康检查),进一步占用资源。
2、单线程 + 多路IO复用
(1) 解释:
一个线程 ,处理多个客户端。采用多路I/O复用机制(epoll) 监控多个fd的IO请求。
多个请求是依次执行的,fd1的请求完成了,才去处理fd2的请求。
(2) 优点: 只需要一个线程,节省资源。
(3) 缺点:同一时间只能处理一个客户端操作,会有排队延迟
3、单线程 + 多路IO复用 + 多线程处理业务
(1) 解释:
把逻辑任务分离到另外的线程池。
创建专门用于处理逻辑业务的线程池。
当有客户端消息到达,主线程完成io读操作后,将后续逻辑业务放入线程池的一个线程中执行,主线程继续监控IO请求。当线程执行完逻辑业务之后,将结果消息返回主线程,主线再发送给客户端。
(2) 优点: 将业务放到线程处理,减轻排队延迟。
(3) 缺点:IO 读写数据时的并发能力依然是1
4、单线程(只监听) + 多路IO复用 + 多线程处理业务和IO读写
(1) 解释:
在3的基础上,让线程池把 IO读写 和 业务 都处理了。
主线程只负责用来创建fd,每个线程负责监听多个客户端读写操作,以及后续逻辑业务。
使用例子: GO语言的 原生net 和 netpoll 采用的是这个模式。
(2) 优点:
a. 可以监听更多的fd,提升io读写的并发能力
b. 充分利用多核CPU
(3) 缺点:同一个线程内的多个客户端,依然是排队的
三、Netpoll与原生GoNet对比
1. 触发方式
Go Net:使用 边缘触发。只有一个Epoll事件循环,高并发时压力大。但消息包很大时,需要内存少。
NetPoll:使用 水平触发。支持多个事件,高并发效果更好。但消息包很大时,需要更多的内存。
2. Goroutine数目
Go Net:一个Goroutine下只有一个连接。
NetPoll:一个Goroutine下有多个连接。
3. 是否支持共用buff池,少拷贝一次数据。
Go Net:不支持。
NetPoll:支持。管理一个Buffer池直接交给用户,少拷贝一次。
四、边缘触发和水平触发的区别
1. 水平触发(LT): 若就绪的事件一次没有处理完,就会将没有处理完的事件继续放回到就绪队列之中,下一次可继续触发事件。
必须使用非阻塞模式。因为水平触发模式会将文件描述符一直读到返回 errno或EAGAIN,非阻塞模式下才会返回这个。 而阻塞模式下不会返回这个,所以会一直阻塞卡死。
2. 边缘触发(ET):就绪的事件只能处理一次,需要一次过把数据读完。
若没有处理完,会在下次的其它事件就绪时再进行处理。
而若以后再也没有就绪的事件,剩余的那部分数据也会丢失。