第八章 高性能服务器程序框架
Reactor与Proactor
基本概念
1. Reactor
主线程只负责监听文件描述符是否有事件发生,有的话通知工作线程,除此之外,不做任何其他事。读写数据,接受新的连接请求,处理客户请求都在工作线程中完成
2. Proactor
特点是异步I/O模型。
I/O操作由内核完成,工作线程只负责处理将I/O操作提交给内核的前面的操作(如告诉内核需要操作I/O了,告诉内核用户写缓冲区)
3. 模拟Proactor
主线程负责数据读写,当数据读写完了后,插入请求队列,由工作线程竞争获得。从工作线程的角度来看,他们直接获得了数据的读写结果,接下来只需要对读写的结果做逻辑处理。
也就是说工作线程在操作任务时,已经完成了对数据的读写操作,只需要处理其他操作就行了。
两种高效的并发模型
- 半同步/半异步模型
- 领导者/追随者模型
这个同步和异步不是IO模型中的那个同步异步(IO是否内核处理)。
在并发模型中,同步是指程序完全按照代码顺序执行,异步是指程序执行需要由系统事件来驱动。
半同步/半异步模型
同步线程用于处理客户逻辑,异步线程处理IO事件
第十章 信号
统一信号源
将信号和IO一样,一起放到epoll处理。
怎么实现呢?
- 利用socketpair函数创建管道套接字,将写端设置成非阻塞后,将本地管道的读端注册到epoll事件表中。
- 随后,注册信号处理函数:信号处理函数(回调函数)设置成当信号产生,就往管道的写端发送数据。
- 最后,当信号产生并往管道的写端写了数据后,主循环通过**epoll_wait()**返回的事件表得知,信号产生了。然后开始处理信号。
第十一章 定时器
处理非活动连接
服务器需要定期处理非活动连接,不然会占用资源。
定时器在web服务器的作用只是为了每个单位时间提醒一下服务器去检查是否有超时时间。如果有就执行回调函数关闭连接。(具体来说就是每个单位时间产生SIGALRM信号,主循环接收到这个信号就会去检查是否有超时)
注意,每个连接设置的时间是绝对时间,也就是什么时候到这个时间点,如果到这个时间点则说明过期了。
在将定时器设置成统一信号源后,在主循环内就要判断是否定时器到期(查看本地管道的读端是否事件产生)。如果产生了就需要处理信号(注意,书中只写了处理两种信号,SIGALRM----定时器和SIGTERM----终止程序)。如果是SIGALRM定时器,就需要检查一遍链表中是否有连接已经超时了,超时就会自动执行回调函数,并重新开始计时器。
在处理非活动连接时,如果客户发送数据了,则要重置超时时间,并将该节点重新插入定时器链表,保持有序。以及如果客户关闭连接了,节点也记得释放。总之就是要记得维护定时器链表
怎么实现处理非活动连接呢?
- 初始化 FD_LIMIT 个计时器节点,维护一个定时器链表(按超时时间升序)。
- 在有新客户连接时,创建定时器,设置回调函数与超时事件,将定时器与用户绑定,最后添加到定时器链表中。
- 在客户数据到达时,需要重新更新计时器事件。
- 在客户关闭连接时,节点记得释放。
- 设置回调函数,服务器会根据定时器定期检查是否有超时节点,当确实产生超时了,该节点会自动调用回调函数关闭本次连接。