前言
在进行linux网络编程之前,我们需要来了解整个项目的流程。
事务处理模式 Reactor 、 Proactor
在webserver服务器中,针对常见的三类事务:I/O事务、信号定时事件、逻辑事务。对于事务的处理模式,有两种高效的方式:
Reactor和Proactor模式。其中同步I/O通常用于实现Reactor模式,异步I/O模型通常用于实现Proactor模式
Reactor模式
主线程(I/O处理单元)只负责监听文件描述符上是否有事件发生,有的话就立即将该事件通知工作线程(逻辑单元),将 socket 可读可写事件放入请求队列,交给工作线程处理。除此之外,主线程不做任何其他实质性的工作。读写数据,接受新的连接,以及处理客户请求均在工作线程中完成。
使用同步 I/O(以 epoll_wait 为例)实现的 Reactor 模式的工作流程是:
- 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。
- 主线程调用 epoll_wait 等待 socket 上有数据可读。
- 当 socket 上有数据可读时, epoll_wait 通知主线程。主线程则将 socket 可读事件放入请求队列。
- 睡眠在请求队列上的某个工作线程被唤醒,它从 socket 读取数据,并处理客户请求,然后往 epoll
内核事件表中注册该 socket 上的写就绪事件。 - 当主线程调用 epoll_wait 等待 socket 可写。
- 当 socket 可写时,epoll_wait 通知主线程。主线程将 socket 可写事件放入请求队列。
- 睡眠在请求队列上的某个工作线程被唤醒,它往 socket 上写入服务器处理客户请求的结果
Proactor
Proactor 模式将所有 I/O 操作都交给主线程和内核来处理(进行读、写),工作线程仅仅负责业务逻辑。使用异步 I/O 模型(以 aio_read 和 aio_write 为例)实现的 Proactor 模式的工作流程是:
- 主线程调用 aio_read 函数向内核注册 socket 上的读完成事件,并告诉内核用户读缓冲区的位置,以及读操作完成时如何通知应用程序(这里以信号为例)。
- 主线程继续处理其他逻辑。
- 当 socket 上的数据被读入用户缓冲区后,内核将向应用程序发送一个信号,以通知应用程序数据已经可用。
- 应用程序预先定义好的信号处理函数选择一个工作线程来处理客户请求。工作线程处理完客户请求后,调用 aio_write 函数向内核注册 socket 上的写完成事件,并告诉内核用户写缓冲区的位置,以及写操作完成时如何通知应用程序。
- 主线程继续处理其他逻辑。
- 当用户缓冲区的数据被写入 socket 之后,内核将向应用程序发送一个信号,以通知应用程序数据已经发送完毕。
- 应用程序预先定义好的信号处理函数选择一个工作线程来做善后处理,比如决定是否关闭 socket。
同步I/O模拟Proactor模式
使用同步 I/O 方式模拟出 Proactor 模式。原理是:主线程执行数据读写操作,读写完成之后,主线程向
工作线程通知这一”完成事件“。那么从工作线程的角度来看,它们就直接获得了数据读写的结果,接下
来要做的只是对读写的结果进行逻辑处理。
使用同步 I/O 模型(以 epoll_wait为例)模拟出的 Proactor 模式的工作流程如下:
- 主线程往 epoll 内核事件表中注册 socket 上的读就绪事件。
- 主线程调用 epoll_wait 等待 socket 上有数据可读。
- 当 socket 上有数据可读时,epoll_wait 通知主线程。主线程从 socket 循环读取数据,直到没有更
多数据可读,然后将读取到的数据封装成一个请求对象并插入请求队列。 - 睡眠在请求队列上的某个工作线程被唤醒,它获得请求对象并处理客户请求,然后往 epoll 内核事
件表中注册 socket 上的写就绪事件。 - 主线程调用 epoll_wait 等待 socket 可写。
- 当 socket 可写时,epoll_wait 通知主线程。主线程往 socket 上写入服务器处理客户请求的结果。
基本架构模块介绍
在项目开篇介绍中,我们了解了webserver的基本架构,例如I/O处理单元,逻辑单元记忆网络存储单元三大块内容,接下来我们将分别解析三块内容所需要的详细技术。
I/O单元:服务器的主线程,通过socket进行I/O通信。服务器在主线程建立socket,并监听服务器端口。随后客户端与服务器建立连接通信。
逻辑单元 在Reactor模式下,逻辑单元只负责处理语义逻辑,I/O数据的读取。而针对多个客户提出不同的请求,如果我们使用单一线程处理请求,效率低下。因此逻辑单元部分一般都采用多线程或者多进程方式处理;这里需要了解到一个关键技术线程池的概念。
虽然线程相对于进程来说,创建消耗的资源较少,但是还是存在创建、销毁线程的资源消耗。因此为了不频繁创建线程,我们引入多线程概念,多线程的出现使得线程创建后可以被循环使用,从而减少了大量的创建、销毁操作开销。
网络存储单元 这一部分需要用到数据库相关技术,服务器连接数据库并对数据库进行相关操作,从而对服务器本身的数据操作。(详细内容后续补充)
总结
本小结针对webserver的事务处理逻辑以及架构进行了简要分析,后续将一一对每一模块的具体实现进行介绍分析。
【参考文献】
TinyWebServer:
https://github.com/qinguoyi/TinyWebServer
小白视角:一文读懂社长的TinyWebServer:
huixxi.github.io/2020/06/02/小白视角:一文读懂社长的TinyWebServer
Linux 高并发服务器开发:
https://www.nowcoder.com/courses/cover/live/504