【网络编程笔记六】IO复用模型+reactor模型以及应用场景

本文记录以下几个问题:
1、IO复用函数的一些记录,包括:
对select、poll和epoll函数的对比;
水平触发和边缘触发的使用场景;
关闭tcp连接时,应该先将fd从epoll结构中移出还是先调用close?
2、文件描述符的计数。
3、关于Reactor模型的一些记录,包括:
reactor和proactor的区别;
在接收请求/响应请求的模型中,一次recv完后,为什么要先check一下有没有读完数据?
4、Reactor模型的应用场景举例,包括:
单reactor模型的redis;
多reactor多线程模型的memacahed;
多reactor多进程模型的nginx。

1、select/poll/epoll函数的一些笔记

1.1、select函数

int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);

select函数的fd_set:
select()机制中提供一个fd_set的数据结构,实际上是一long类型的数组,一共可存放1024个bit位,以fd为下标作索引。因此,如果fd的值超过1024的话,select就会报错。
fd_set的原型:long 数组[1024/(8*sizeof(long))]。

1.2、poll函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd{
	int fd;	        //文件描述符
	short events;	//等待的事件
	short revents;	//实际发生的事件
};

poll和select的调用本质是一样的,区别是:
poll没有1024个文件描述符的限制,然后参数不用把读/写/error分三个参数传入,而是通过一个结构体数组传入。
虽然poll没有1024个文件描述符的限制但是数量过大后性能也是会下降。poll() 和 select() 同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

1.3、epoll函数

int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

typedef union epoll_data {
	void ptr;
	int fd;
	__uint32_t u32;
	__uint64_t u64;
} epoll_data_t;
 
struct epoll_event {
	__uint32_t events;    / Epoll events /
	epoll_data_t data;    / User data variable /
};

epoll_create函数:创建红黑树和就绪队列。
epoll_ctl函数:操作这棵红黑树,和内核建立回调关系。
epoll_wait:一旦有事件就绪,就放入就绪队列中。epoll_wait的timeout参数:-1永远阻塞,0不阻塞,大于0为阻塞时间。
epoll_wait的返回值:异常返回-1,到期时没有就绪事件返回0,成功且有就绪事件返回就绪的事件个数。
水平触发和边缘触发的使用场景:
1.listenfd要用水平触发,因为如果监听fd用边缘触发的话,那么在多个连接同时进来的时候,可能会漏处理一些连接。
2.小数据的读取用边缘触发。比如每次recv的buffer能一次性读完,那就是小数据。
3.在需要界定数据包(比如做协议的解析)、做业务逻辑的时候,就需要使用水平触发;做反向代理的时候,对界定数据包不是很重要的时候,使用边缘触发的效率更高。
关闭tcp连接时,应该先将fd从epoll结构中移出还是先调用close?
关闭套接字时,要先从epoll中把fd移出,然后再调用close,避免close后,还有epollin事件出现就绪。如果先调用close的话,由于该连接的tcb已经从内核中移除了,这时候再去调用epoll的移除,可能会有一些副作用。

2、文件描述符的计数

fd的值是基于一个进程的,比如:同一个机器下,进程A的fd从0/1/2开始往上加,进程B同样也是,两个进程的fd互不影响。

3、关于Reactor模型的一些记录

reactor反应堆:对fd的集中管理,对于不同的fd的事件,调用对应的回调函数。

3.1、reactor和proactor的区别

这里参考在知乎上看到其他大佬的描述:
Reactor 可以理解为「来了事件操作系统通知应用进程,让应用进程来处理」,而 Proactor 可以理解为「来了事件操作系统来处理,处理完再通知应用进程」。 无论是 Reactor,还是 Proactor,都是一种基于「事件分发」的网络编程模式,区别在于 Reactor 模式是基于「待完成」的 I/O 事件,而 Proactor 模式则是基于「已完成」的 I/O 事件。
我的理解是:Proactor就是让操作系统内核去处理IO事件,上层把应用层缓冲区提供给系统内核,然后内核读取到的数据后,直接存放到上层的缓冲区并通知上层。

3.2、在接收请求/响应请求的模型中,一次recv完后,为什么要先check一下有没有读完数据?

一般是check一下应用层协议的body-length有没有读完,如果没有读完的话,可能是因为内核还没收完对面的tcp数据,这时候就不能直接设置send事件了,而是要继续监听读事件(如果是epoll的水平触发就比较方便,下次还会触发读事件),直到判断消息读完。

3.3、紧接3.2的场景,在读取完对方的请求后,可以直接调用send函数发送响应吗?

建议不要这样做,而是应该:重设写事件的监听(比如epoll模型中,设置epollout状态)。因为如果直接调send,可能这时候内核发送缓冲区还没有空闲空间,此时,遇到非阻塞的socket会直接报错,遇到阻塞的socket就block住。因此,把send事件先挂起到epoll的IO复用中,当内核发送缓冲区可写时,由epoll把事件置为就绪状态,然后这时候再去send就肯定成功了。

4、Reactor模型的应用场景举例

4.1、单reactor模型的redis

redis6.0的IO多线程的做法:
主线程维护了两个队列,一个读队列一个写队列,读到数据后,将数据封装并放到队列中,然后交给子线程去处理;子线程要写数据时,将数据放到写队列中,然后交给主线程去处理。
为什么redis可以使用单reactor?
因为redis数据结构的优化比较好,而且大多数命令的操作比较简单,单reactor模型可以处理这些业务。

4.2、多reactor多线程模型的memacahed

多reactor模型,英文名:one eventloop per thread。做法就是:用一个reactor模型来处理连接监听,用多个reactor模型来处理后续的IO事件。
适合reactor的场景:
当每个连接的之间的业务逻辑没有相关性时(临界资源少,需要加锁的地方少),这种模型的效率才比单reactor模型高。而当每个连接之间的处理逻辑具有相关性,那么这种模型的效率是反而更低的。
memacahed模型图如下:
在这里插入图片描述
为什么要专门一个reactor线程来处理连接建立的监听的?
因为TCP的全连接队列我们习惯设为128,如果当客户端连接发起的并发量大时,可能会一直在处理这些accept,会使得其它IO事件被延迟处理,因此单独一个reactor线程来处理连接建立的监听。
mysql就是使用一个select专门来检测数据的连接,然后accept后,再放到其他IO复用线程去监听读写事件。

4.3、多reactor多进程模型的nginx

多个worker进程同时监听相同端口,中间有一把共享锁和一个共享内存,连接到来时,由其中一个worker进程去处理连接和后续的IO事件。每个worker内也是一个reactor。

4.4、redis和memacahed都用了epoll的水平触发,而nginx用的是边缘触发,为什么?

因为nginx主要用来做反向代理,它只要一收到数据,就全部读取出来,然后转发给其他后端服务,在这个场景下nginx不需要界定数据包,因此选择边缘触发效率更高。
而redis和memacahed是以业务为导向的,需要去界定数据包的,因此用水平触发更合适。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值